Document number:  PL22.16/08-0199 = WG21 N2689
Date:  2008-06-29
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 Defect Reports, Revision 56


This document contains the C++ core language issues that have been categorized as Defect Reports by the Committee (J16 + WG21), that is, issues with status "DR," "WP," and "TC1," along with their proposed resolutions. ONLY RESOLUTIONS FOR ISSUES WITH TC1 STATUS ARE PART OF THE INTERNATIONAL STANDARD FOR C++. The other issues are provided for informational purposes only, as an indication of the intent of the Committee. They should not be considered definitive until or unless they appear in an approved Technical Corrigendum or revised International Standard for C++.

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

For more information, including a description of the meaning of the issue status codes and instructions on reporting new issues, please see the Active Issues List.

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


Issues with "DR" Status


666. Dependent qualified-ids without the typename keyword

Section: 14.6  [temp.res]     Status: DR     Submitter: Daveed Vandevoorde     Date: 6 December 2007
[Voted into the WP at the June, 2008 meeting.]

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]




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

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

[Voted into the WP at the June, 2008 meeting as paper N2657.]

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

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.

Notes from the April, 2007 meeting:

The Evolution Working Group is now actively pursuing an extension that would allow local and/or nameless types to be used as template arguments, so this resolution will be held in abeyance until the outcome of that proposal is known.

Notes from the June, 2008 meeting:

Paper N2657, adopted at the Sophia Antipolis (June, 2008) meeting, removed the restriction against local and unnamed types as template parameters. The example is now well-formed.




657. Abstract class parameter in synthesized declaration

Section: 14.8.2  [temp.deduct]     Status: DR     Submitter: Mike Miller     Date: 31 October 2007

[Voted into the WP at the June, 2008 meeting as paper N2634.]

A customer of ours recently brought the following example to our attention. There's some question as to whether the Standard adequately addresses this example, and if it does, whether the outcome is what we'd like to see. Here's the example:

    struct Abs {
      virtual void x() = 0;
    };

    struct Der: public Abs {
      virtual void x();
    };

    struct Cnvt {
      template <typename F> Cnvt(F);
    };

    void foo(Cnvt a);
    void foo(Abs &a);

    void f() {
      Der d;
      Abs *a = &d;
      foo(*a);        // #1
      return 0;
    }

The question is how to perform overload resolution for the call at #1. To do that, we need to determine whether foo(Cnvt) is a viable function. That entails deciding whether there is an implicit conversion sequence that converts Abs (the type of *a in the call) to Cnvt (13.3.2 [over.match.viable] paragraph 3), and that involves a recursive invocation of overload resolution.

The initialization of the parameter of foo(Cnvt) is a case of copy-initialization of a class by user-defined conversion, so the candidate functions are the converting constructors of Cnvt (13.3.1.4 [over.match.copy] paragraph 1), of which there are two: the implicitly-declared copy constructor and the constructor template.

According to 14.7.1 [temp.inst] paragraph 8,

If a function template or a member function template specialization is used in a way that involves overload resolution, a declaration of the specialization is implicitly instantiated (14.8.3 [temp.over]).

Template argument deduction results in “synthesizing” (14.8.3 [temp.over] paragraph 1) (or “instantiating,” 14.7.1 [temp.inst] paragraph 8) the declaration

    Cnvt::Cnvt(Abs)

Because Abs is an abstract class, this declaration violates the restriction of 10.4 [class.abstract] paragraph 3 (“An abstract class shall not be used as a parameter type...”), and because a parameter of an abstract class type does not cause a deduction failure (it's not in the bulleted list in 14.8.2 [temp.deduct] paragraph 2), the program is ill-formed. This error is reported by both EDG and Microsoft compilers, but not by g++.

It seems unfortunate that the program would be rendered ill-formed by a semantic violation in a declaration synthesized solely for the purpose of overload resolution analysis; foo(Cnvt) would not be selected by overload resolution, so Cnvt::Cnvt(Abs) would not be instantiated.

There's at least some indication that a parameter with an abstract class type should be a deduction failure; an array element of abstract class type is a deduction failure, so one might expect that a parameter would be, also.

(See also issue 339; this question might be addressed as part of the direction described in the notes from the July, 2007 meeting.)

Notes from the June, 2008 meeting:

Paper N2634, adopted at the June, 2008 meeting, makes the error a deduction failure, so the code is now well-formed.




485. What is a “name”?

Section: 3  [basic]     Status: DR     Submitter: Gabriel Dos Reis     Date: 9 Nov 2004
[Voted into the WP at the June, 2008 meeting.]

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: DR     Submitter: fvali     Date: 31 July 1999
[Voted into the WP at the June, 2008 meeting.]

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

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

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

Proposed Resolution (November, 2006):

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

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



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

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

[Voted into the WP at the June, 2008 meeting.]

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

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

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

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

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

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

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

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

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

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

Proposed Resolution (10/98):

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

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

Notes (November, 2006):

C89 is slightly contradictory here: It allows any signal handler to terminate by calling abort, exit, longjmp, but (for asynchronous signals, i.e. not those produced by abort or raise) then makes calling any library function other than signal with the current signal undefined behavior (C89 7.7.1.1). For synchronous signals, C99 forbids calls to raise, but imposes no other restrictions. For asynchronous signals, C99 allows only calls to abort, _Exit, and signal with the current signal (C99 7.14.1.1). The current C++ WP refers to “plain old functions” and “conforming C programs” (18.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: DR     Submitter: Alisdair Meredith     Date: 8 Aug 2007
[Voted into the WP at the June, 2008 meeting.]

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

    struct pixel {
        int x, y;
    };

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

Proposed resolution (February, 2008):

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

A type is a literal type if it is:



654. Conversions to and from nullptr_t

Section: 4.10  [conv.ptr]     Status: DR     Submitter: Jason Merrill     Date: 7 October 2007

[Voted into the WP at the June, 2008 meeting as paper N2656.]

In the interest of promoting use of nullptr instead of the integer literal 0 as the null pointer constant, the proposal accepted by the Committee does not provide for converting a zero-valued integral constant to type std::nullptr_t. However, this omission reduces the utility of the feature for use in the library for smart pointers. In particular, the addition of that conversion (along with a converting constructor accepting a std::nullptr_t) would allow smart pointers to be used just like ordinary pointers in expressions like:

    if (p == 0) { }
    if (0 == p) { }
    if (p != 0) { }
    if (0 != p) { }
    p = 0;

The existing use of the “unspecified bool type” idiom supports this usage, but being able to use std::nullptr_t instead would be simpler and more elegant.

Jason Merrill: I have another reason to support the conversion as well: it seems to me very odd for nullptr_t to be more restrictive than void*. Anything we can do with an arbitrary pointer, we ought to be able to do with nullptr_t as well. Specifically, since there is a standard conversion from literal 0 to void*, and there is a standard conversion from void* to bool, nullptr_t should support the same conversions.

This changes two of the example lines in the proposal as adopted:

    if (nullptr) ;      // error, no conversion to bool
    if (nullptr == 0) ; // error

become

    if (nullptr) ;      // evaluates to false
    if( nullptr == 0 ); // evaluates to true

And later,

    char* ch3 = expr ? nullptr : nullptr; // ch3 is the null pointer value
    char* ch4 = expr ? 0 : nullptr;       // ch4 is the null pointer value
    int n3 = expr ? nullptr : nullptr;    // error, nullptr_t can’t be converted to int
    int n4 = expr ? 0 : nullptr;          // error, nullptr_t can’t be converted to int

I would also allow reinterpret_cast from nullptr_t to integral type, with the same semantics as a reinterpret_cast from the null pointer value to integral type.

Basically, I would like nullptr_t to act like a void* which is constrained to always be (void*)0.




339. Overload resolution in operand of sizeof in constant expression

Section: 5.19  [expr.const]     Status: DR     Submitter: Steve Adamczyk     Date: 11 Mar 2002

[Voted into the WP at the June, 2008 meeting as paper N2634.]

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;
  }

  template<typename T>
  A<sizeof(f(T()))> make_A() {
    return A<sizeof(f(T()))>();
  }

  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>(make_A<short>()));
    printf("int *: %d\n", conv_int2<int *>(make_A<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.

Notes from the April, 2007 meeting:

The CWG is pursuing a compromise proposal, to which the EWG has tentatively agreed, which would allow arbitrary expressions in the return types of function templates but which would restrict the expressions that participate in the function signature (and thus in overload resolution) to those that can be used as non-type template arguments. During deduction and overload resolution, these complex return types would be ignored; that is, there would be no substitution of the deduced template arguments into the return type at this point. If such a function were selected by overload resolution, however, a substitution failure in the return type would produce a diagnostic rather than a deduction failure.

This approach works when doing overload resolution in the context of a function call, but additional tricks (still being defined) are needed in other contexts such as friend function declaration matching and taking the address of a function, in which the return type does play a part.

Notes from the July, 2007 meeting:

The problem is whether arbitrary expressions (for example, ones that include overload resolution) are allowed in template deduction contexts, and, if so, which expression errors are SFINAE failures and which are hard errors.

This issue deals with arbitrary expressions inside sizeof in deduction contexts. That's a fringe case right now (most compilers don't accept them). decltype makes the problem worse, because the standard use case is one that involves overload resolution. Generalized constant expressions make it worse yet, because they allow overload resolution and class types to show up in any constant expression in a deduction context.

Why is this an issue? Why don't we just say everything is allowed and be done with it?

At the April, 2007 meeting, we were headed toward a solution that imposed a restriction on expressions in deduction contexts, but such a restriction seems to really hamper uses of constexpr functions. So we're now proposing that fully general expressions be allowed, and that most errors in such expressions be treated as SFINAE failures rather than errors.

One issue with writing Standard wording for that is how to define “most.” There's a continuum of errors, some errors being clearly SFINAE failures, and some clearly “real” errors, with lots of unclear cases in between. We decided it's easier to write the definition by listing the errors that are not treated as SFINAE failures, and the list we came up with is as follows:

  1. errors that occur while processing some entity external to the expression, e.g., an instantiation of a template or the generation of the definition of an implicitly-declared copy constructor
  2. errors due to implementation limits
  3. errors due to access violations (this is a judgment call, but the philosophy of access has always been that it doesn't affect visibility)

Everything else produces a SFINAE failure rather than a hard error.

There was broad consensus that this felt like a good solution, but that feeling was mixed with trepidation on several fronts:

We will be producing wording for the Working Draft for the October, 2007 meeting.

(See also issue 657.)




118. Calls via pointers to virtual member functions

Section: 5.2.2  [expr.call]     Status: DR     Submitter: Martin O'Riordan     Date: 17 May 1999
[Voted into the WP at the June, 2008 meeting.]

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: DR     Submitter: James Kuyper     Date: 19 May 2001
[Voted into the WP at the June, 2008 meeting.]

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: DR     Submitter: Daveed Vandevoorde     Date: 27 November 2007
[Voted into the WP at the June, 2008 meeting.]

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: DR     Submitter: James Kanze     Date: 28 Mar 2001
[Voted into the WP at the June, 2008 meeting.]

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: DR     Submitter: Steve Clamage     Date: 30 November 2007
[Voted into the WP at the June, 2008 meeting.]

The C99 and C++ Standards disagree about the validity of two Cyrillic characters for use in identifiers. C++ (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 "WP" Status


357. Definition of signature should include name

Section: 1.3  [intro.defs]     Status: WP     Submitter: Steve Clamage     Date: 26 May 2002

[Voted into WP at April, 2007 meeting.]

Section 1.3 [intro.defs], definition of "signature" omits the function name as part of the signature. Since the name participates in overload resolution, shouldn't it be included in the definition? I didn't find a definition of signature in the ARM, but I might have missed it.

Fergus Henderson: I think so. In particular, 17.4.3.2.2 [global.names] reserves certain "function signatures" for use by the implementation, which would be wrong unless the signature includes the name.

-2- Each global function signature declared with external linkage in a header is reserved to the implementation to designate that function signature with external linkage.

-5- Each function signature from the Standard C library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++" linkage, or as a name of namespace scope in the global namespace.

Other uses of the term "function signature" in the description of the standard library also seem to assume that it includes the name.

James Widman:

Names don't participate in overload resolution; name lookup is separate from overload resolution. However, the word “signature” is not used in clause 13 [over]. It is used in linkage and declaration matching (e.g., 14.5.6.1 [temp.over.link]). This suggests that the name and scope of the function should be part of its signature.

Proposed resolution (October, 2006):

  1. Replace 1.3 [intro.defs] “signature” with the following:

  2. 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]
  3. Delete paragraph 3 and replace the first sentence of 14.5.6.1 [temp.over.link] as follows:

  4. The signature of a function template specialization consists of the signature of the function template and of the actual template arguments (whether explicitly specified or deduced).

    The signature of a function template consists of its function signature, its return type and its template parameter list is defined in 1.3 [intro.defs]. The names of the template parameters are significant...

(See also issue 537.)




537. Definition of “signature”

Section: 1.3  [intro.defs]     Status: WP     Submitter: Daveed Vandevoorde     Date: 12 October 2005

[Voted into WP at April, 2007 meeting.]

The standard defines “signature” in two places: 1.3 [intro.defs] and 14.5.6.1 [temp.over.link] paragraphs 3-4. The former seems to be meant as a formal definition (I think it's the only place covering the nontemplate case), yet it lacks some bits mentioned in the latter (specifically, the notion of a “signature of a function template,” which is part of every signature of the associated function template specializations).

Also, I think the 1.3 [intro.defs] words “the information about a function that participates in overload resolution” isn't quite right either. Perhaps, “the information about a function that distinguishes it in a set of overloaded functions?”

Eric Gufford:

In 1.3 [intro.defs] the definition states that “Function signatures do not include return type, because that does not participate in overload resolution,” while 14.5.6.1 [temp.over.link] paragraph 4 states “The signature of a function template consists of its function signature, its return type and its template parameter list.” This seems inconsistent and potentially confusing. It also seems to imply that two identical function templates with different return types are distinct signatures, which is in direct violation of 13.3 [over.match]. 14.5.6.1 [temp.over.link] paragraph 4 should be amended to include verbiage relating to overload resolution.

Either return types are included in function signatures, or they're not, across the board. IMHO, they should be included as they are an integral part of the function declaration/definition irrespective of overloads. Then verbiage should be added about overload resolution to distinguish between signatures and overload rules. This would help clarify things, as it is commonly understood that overload resolution is based on function signature.

In short, the term “function signature” should be made consistent, and removed from its (implicit, explicit or otherwise) linkage to overload resolution as it is commonly understood.

James Widman:

The problem is that (a) if you say the return type is part of the signature of a non-template function, then you have overloading but not overload resolution on return types (i.e., what we have now with function templates). I don't think anyone wants to make the language uglier in that way. And (b) if you say that the return type is not part of the signature of a function template, you will break code. Given those alternatives, it's probably best to maintain the status quo (which the implementors appear to have rendered faithfully).

Proposed resolution (September, 2006):

This issue is resolved by the resolution of issue 357.




513. Non-class “most-derived” objects

Section: 1.8  [intro.object]     Status: WP     Submitter: Marc Schoolderman     Date: 20 Mar 2005

[Voted into WP at April, 2006 meeting.]

The standard uses “most derived object” in some places (for example, 1.3 [intro.defs] “dynamic type,” 5.3.5 [expr.delete]) to refer to objects of both class and non-class type. However, 1.8 [intro.object] only formally defines it for objects of class type.

Possible fix: Change the wording in 1.8 [intro.object] paragraph 4 from

an object of a most derived class type is called a most derived object

to

an object of a most derived class type, or of non-class type, is called a most derived object

Proposed resolution (October, 2005):

Add the indicated words to 1.8 [intro.object] paragraph 4:

If a complete object, a data member (9.2 [class.mem]), or an array element is of class type, its type is considered the most derived class, to distinguish it from the class type of any base class subobject; an object of a most derived class type, or of a non-class type, is called a most derived object.



484. Can a base-specifier name a cv-qualified class type?

Section: 10  [class.derived]     Status: WP     Submitter: Richard Corden     Date: 21 Oct 2004

[Voted into WP at the October, 2006 meeting.]

Issue 298, recently approved, affirms that cv-qualified class types can be used as nested-name-specifiers. Should the same be true for base-specifiers?

Rationale (April, 2005):

The resolution of issue 298 added new text to 9.1 [class.name] paragraph 5 making it clear that a typedef that names a cv-qualified class type is a class-name. Because the definition of base-specifier simply refers to class-name, it is already the case that cv-qualified class types are permitted as base-specifiers.

Additional notes (June, 2005):

It's not completely clear what it means to have a cv-qualified type as a base-specifier. The original proposed resolution for issue 298 said that “the cv-qualifiers are ignored,” but that wording is not in the resolution that was ultimately approved.

If the cv-qualifiers are not ignored, does that mean that the base-class subobject should be treated as always similarly cv-qualified, regardless of the cv-qualification of the derived-class lvalue used to access the base-class subobject? For instance:

    typedef struct B {
        void f();
        void f() const;
        int i;
    } const CB;

    struct D: CB { };

    void g(D* dp) {
        dp->f();    // which B::f?
        dp->i = 3;  // permitted?
    }

Proposed resolution (October, 2005):

  1. Change 9.1 [class.name] paragraph 5 as indicated:

  2. A typedef-name (7.1.3 [dcl.typedef]) that names a class type, or a cv-qualified version thereof, is also a class-name, but class-name. If a typedef-name that names a cv-qualified class type is used where a class-name is required, the cv-qualifiers are ignored. A typedef-name shall not be used as the identifier in a class-head.
  3. Delete 7.1.3 [dcl.typedef] paragraph 8:

  4. [Note: if the typedef-name is used where a class-name (or enum-name) is required, the program is ill-formed. For example,

        typedef struct {
            S();     // error: requires a return type because S is
                      // an ordinary member function, not a constructor
        } S;
    

    end note]




39. Conflicting ambiguity rules

Section: 10.2  [class.member.lookup]     Status: WP     Submitter: Neal M Gafter     Date: 20 Aug 1998

[Voted into WP at April 2005 meeting.]

The ambiguity text in 10.2 [class.member.lookup] may not say what we intended. It makes the following example ill-formed:

    struct A {
        int x(int);
    };
    struct B: A {
        using A::x;
        float x(float);
    };
    
    int f(B* b) {
        b->x(3);  // ambiguous
    }
This is a name lookup ambiguity because of 10.2 [class.member.lookup] paragraph 2:
... Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declaration designated by the using-declaration. If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed.
This contradicts the text and example in paragraph 12 of 7.3.3 [namespace.udecl] .

Proposed Resolution (10/00):

  1. Replace the two cited sentences from 10.2 [class.member.lookup] paragraph 2 with the following:

    The resulting set of declarations shall all be from sub-objects of the same type, or there shall be a set of declarations from sub-objects of a single type that contains using-declarations for the declarations found in all other sub-object types. Furthermore, for nonstatic members, the resulting set of declarations shall all be from a single sub-object, or there shall be a set of declarations from a single sub-object that contains using-declarations for the declarations found in all other sub-objects. Otherwise, there is an ambiguity and the program is ill-formed.
  2. Replace the examples in 10.2 [class.member.lookup] paragraph 3 with the following:

        struct A {
            int x(int);
            static int y(int);
        };
        struct V {
            int z(int);
        };
        struct B: A, virtual V {
            using A::x;
            float x(float);
            using A::y;
            static float y(float);
            using V::z;
            float z(float);
        };
        struct C: B, A, virtual V {
        };
    
        void f(C* c) {
            c->x(3);    // ambiguous -- more than one sub-object A
            c->y(3);    // not ambiguous
            c->z(3);    // not ambiguous
        }
    

Notes from 04/01 meeting:

The following example should be accepted but is rejected by the wording above:

    struct A { static void f(); };

    struct B1: virtual A {
        using A::f;
    };

    struct B2: virtual A {
        using A::f;
    };

    struct C: B1, B2 { };

    void g() {
        C::f();        // OK, calls A::f()
    }

Notes from 10/01 meeting (Jason Merrill):

The example in the issues list:

    struct A {
        int x(int);
    };
    struct B: A {
        using A::x;
        float x(float);
    };
    
    int f(B* b) {
        b->x(3);  // ambiguous
    }
Is broken under the existing wording:
... Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declaration designated by the using-declaration. If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed.
Since the two x's are considered to be "from" different objects, looking up x produces a set including declarations "from" different objects, and the program is ill-formed. Clearly this is wrong. The problem with the existing wording is that it fails to consider lookup context.

The first proposed solution:

The resulting set of declarations shall all be from sub-objects of the same type, or there shall be a set of declarations from sub-objects of a single type that contains using-declarations for the declarations found in all other sub-object types. Furthermore, for nonstatic members, the resulting set of declarations shall all be from a single sub-object, or there shall be a set of declarations from a single sub-object that contains using-declarations for the declarations found in all other sub-objects. Otherwise, there is an ambiguity and the program is ill-formed.
breaks this testcase:
    struct A { static void f(); };

    struct B1: virtual A {
        using A::f;
    };

    struct B2: virtual A {
        using A::f;
    };

    struct C: B1, B2 { };

    void g() {
        C::f();        // OK, calls A::f()
    }
because it considers the lookup context, but not the definition context; under this definition of "from", the two declarations found are the using-declarations, which are "from" B1 and B2.

The solution is to separate the notions of lookup and definition context. I have taken an algorithmic approach to describing the strategy.

Incidentally, the earlier proposal allows one base to have a superset of the declarations in another base; that was an extension, and my proposal does not do that. One algorithmic benefit of this limitation is to simplify the case of a virtual base being hidden along one arm and not another ("domination"); if we allowed supersets, we would need to remember which subobjects had which declarations, while under the following resolution we need only keep two lists, of subobjects and declarations.

Proposed resolution (October 2002):

Replace 10.2 [class.member.lookup] paragraph 2 with:

The following steps define the result of name lookup for a member name f in a class scope C.

The lookup set for f in C, called S(f,C), consists of two component sets: the declaration set, a set of members named f; and the subobject set, a set of subobjects where declarations of these members (possibly including using-declarations) were found. In the declaration set, using-declarations are replaced by the members they designate, and type declarations (including injected-class-names) are replaced by the types they designate. S(f,C) is calculated as follows.

If C contains a declaration of the name f, the declaration set contains every declaration of f in C (excluding bases), the subobject set contains C itself, and calculation is complete.

Otherwise, S(f,C) is initially empty. If C has base classes, calculate the lookup set for f in each direct base class subjobject Bi, and merge each such lookup set S(f,Bi) in turn into S(f,C).

The following steps define the result of merging lookup set S(f,Bi) into the intermediate S(f,C):

The result of name lookup for f in C is the declaration set of S(f,C). If it is an invalid set, the program is ill-formed.

[Example:

    struct A { int x; };                    // S(x,A) = {{ A::x }, { A }}
    struct B { float x; };                  // S(x,B) = {{ B::x }, { B }}
    struct C: public A, public B { };       // S(x,C) = { invalid, { A in C, B in C }}
    struct D: public virtual C { };         // S(x,D) = S(x,C)
    struct E: public virtual C { char x; }; // S(x,E) = {{ E::x }, { E }}
    struct F: public D, public E { };       // S(x,F) = S(x,E)

    int main() {
      F f;
      f.x = 0;   // OK, lookup finds { E::x }
    }
S(x,F) is unambiguous because the A and B base subobjects of D are also base subobjects of E, so S(x,D) is discarded in the first merge step. --end example]

Turn 10.2 [class.member.lookup] paragraphs 5 and 6 into notes.

Notes from October 2003 meeting:

Mike Miller raised some new issues in N1543, and we adjusted the proposed resolution as indicated in that paper.

Further information from Mike Miller (January 2004):

Unfortunately, I've become aware of a minor glitch in the proposed resolution for issue 39 in N1543, so I'd like to suggest a change that we can discuss in Sydney.

A brief review and background of the problem: the major change we agreed on in Kona was to remove detection of multiple-subobject ambiguity from class lookup (10.2 [class.member.lookup]) and instead handle it as part of the class member access expression. It was pointed out in Kona that 11.2 [class.access.base]/5 has this effect:

If a class member access operator, including an implicit "this->," is used to access a nonstatic data member or nonstatic member function, the reference is ill-formed if the left operand (considered as a pointer in the "." operator case) cannot be implicitly converted to a pointer to the naming class of the right operand.

After the meeting, however, I realized that this requirement is not sufficient to handle all the cases. Consider, for instance,

    struct B {
        int i;
    };

    struct I1: B { };
    struct I2: B { };

    struct D: I1, I2 {
        void f() {
            i = 0;    // not ill-formed per 11.2p5
        }
    };

Here, both the object expression ("this") and the naming class are "D", so the reference to "i" satisfies the requirement in 11.2 [class.access.base]/5, even though it involves a multiple-subobject ambiguity.

In order to address this problem, I proposed in N1543 to add a paragraph following 5.2.5 [expr.ref]/4:

If E2 is a non-static data member or a non-static member function, the program is ill-formed if the class of E1 cannot be unambiguously converted (10.2) to the class of which E2 is directly a member.

That's not quite right. It does diagnose the case above as written; however, it breaks the case where qualification is used to circumvent the ambiguity:

    struct D2: I1, I2 {
        void f() {
            I2::i = 0;    // ill-formed per proposal
        }
    };

In my proposed wording, the class of "this" can't be converted to "B" (the qualifier is ignored), so the access is ill-formed. Oops.

I think the following is a correct formulation, so the proposed resolution we discuss in Sydney should contain the following paragraph instead of the one in N1543:

If E2 is a nonstatic data member or a non-static member function, the program is ill-formed if the naming class (11.2) of E2 cannot be unambiguously converted (10.2) to the class of which E2 is directly a member.

This reformulation also has the advantage of pointing readers to 11.2 [class.access.base], where the the convertibility requirement from the class of E1 to the naming class is located and which might otherwise be overlooked.

Notes from the March 2004 meeting:

We discussed this further and agreed with these latest recommendations. Mike Miller has produced a paper N1626 that gives just the final collected set of changes.

(This resolution also resolves isssue 306.)




306. Ambiguity by class name injection

Section: 10.2  [class.member.lookup]     Status: WP     Submitter: Clark Nelson     Date: 19 Jul 2001

[Voted into WP at April 2005 meeting.]

Is the following well-formed?

    struct A {
        struct B { };
    };
    struct C : public A, public A::B {
        B *p;
    };
The lookup of B finds both the struct B in A and the injected B from the A::B base class. Are they the same thing? Does the standard say so?

What if a struct is found along one path and a typedef to that struct is found along another path? That should probably be valid, but does the standard say so?

This is resolved by issue 39

February 2004: Moved back to "Review" status because issue 39 was moved back to "Review".




390. Pure virtual must be defined when implicitly called

Section: 10.4  [class.abstract]     Status: WP     Submitter: Daniel Frey     Date: 14 Nov 2002

[Voted into WP at March 2004 meeting.]

In clause 10.4 [class.abstract] paragraph 2, it reads:

A pure virtual function need be defined only if explicitly called with the qualified-id syntax (5.1 [expr.prim]).

This is IMHO incomplete. A dtor is a function (well, a "special member function", but this also makes it a function, right?) but it is called implicitly and thus without a qualified-id syntax. Another alternative is that the pure virtual function is called directly or indirectly from the ctor. Thus the above sentence which specifies when a pure virtual function need be defined ("...only if...") needs to be extended:

A pure virtual function need be defined only if explicitly called with the qualified-id syntax (5.1 [expr.prim]) or if implicitly called (12.4 [class.dtor] or 12.7 [class.cdtor]).

Proposed resolution:

Change 10.4 [class.abstract] paragraph 2 from

A pure virtual function need be defined only if explicitly called with the qualified-id syntax (5.1 [expr.prim]).

to

A pure virtual function need be defined only if explicitly called with, or as if with (12.4 [class.dtor]), the qualified-id syntax (5.1 [expr.prim]).

Note: 12.4 [class.dtor] paragraph 6 defines the "as if" cited.




8. Access to template arguments used in a function return type and in the nested name specifier

Section: 11  [class.access]     Status: WP     Submitter: Mike Ball     Date: unknown

[Moved to DR at 4/01 meeting.]

Consider the following example:

    class A {
       class A1{};
       static void func(A1, int);
       static void func(float, int);
       static const int garbconst = 3;
     public:
       template < class T, int i, void (*f)(T, int) > class int_temp {};
       template<> class int_temp<A1, 5, func> { void func1() };
       friend int_temp<A1, 5, func>::func1();
       int_temp<A1, 5, func>* func2();
   };
   A::int_temp<A::A1, A::garbconst + 2, &A::func>* A::func2() {...}
ISSUE 1:

In 11 [class.access] paragraph 5 we have:

This means, if we take the loosest possible definition of "access from a particular scope", that we have to save and check later the following names

      A::int_temp
      A::A1
      A::garbconst (part of an expression)
      A::func (after overloading is done)
I suspect that member templates were not really considered when this was written, and that it might have been written rather differently if they had been. Note that access to the template arguments is only legal because the class has been declared a friend, which is probably not what most programmers would expect.

Rationale:

Not a defect. This behavior is as intended.

ISSUE 2:

Now consider void A::int_temp<A::A1, A::garbconst + 2, &A::func>::func1() {...} By my reading of 11.8 [class.access.nest] , the references to A::A1, A::garbconst and A::func are now illegal, and there is no way to define this function outside of the class. Is there any need to do anything about either of these Issues?

Proposed resolution (04/01):

The resolution for this issue is contained in the resolution for issue 45.




494. Problems with the resolution of issue 45

Section: 11  [class.access]     Status: WP     Submitter: Lloyd J. Lewins     Date: 17 Dec 2004

[Voted into WP at the October, 2006 meeting.]

The proposed resolution for issue 45 inserts the following sentence after 11 [class.access] paragraph 1:

A member of a class can also access all names as the class of which it is a member.

I don't think that this is correctly constructed English. I see two possibilities:

  1. This is a typo, and the correct change is:

    A member of a class can also access all names of the class of which it is a member.

  2. The intent is something more like:

    A member of a nested class can also access all names accessible by any other member of the class of which it is a member.

[Note: this was editorially corrected at the time defect resolutions were being incorporated into the Working Paper to read, “...can also access all the names declared in the class of which it is a member,” which is essentially the same as the preceding option 1.]

I would prefer to use the language proposed for 11.8 [class.access.nest]:

A nested class is a member and as such has the same access rights as any other member.

A second problem is with the text in 11.4 [class.friend] paragraph 2:

[Note: this means that access to private and protected names is also granted to member functions of the friend class (as if the functions were each friends) and to the static data member definitions of the friend class. This also means that private and protected type names from the class granting friendship can be used in the base-clause of a nested class of the friend class. However, the declarations of members of classes nested within the friend class cannot access the names of private and protected members from the class granting friendship. Also, because the base-clause of the friend class is not part of its member declarations, the base-clause of the friend class cannot access the names of the private and protected members from the class granting friendship. For example,
    class A {
        class B { };
        friend class X;
    };
    class X : A::B {     // ill-formed: A::B cannot be accessed
                         // in the base-clause for X
        A::B mx;         // OK: A::B used to declare member of X
        class Y: A::B {  // OK: A::B used to declare member of X
            A::B my;     // ill-formed: A::B cannot be accessed
                         // to declare members of nested class of X
        };
    };
end note]

This seems to be an oversight. The proposed change to 11.8 [class.access.nest] paragraph 1 would appear to have eliminated the restrictions on nested class access. However, at least one compiler (gcc 3.4.3) doesn't appear to take my view, and continues with the restrictions on access by classes within a friend class, while implementing the rest of the resolution of issue 45.

Note (March, 2005):

Andreas Hommel: I think issue 45 requires an additional change in 9.7 [class.nest] paragraph 4:

Like a member function, a friend function (11.4 [class.friend]) defined within a nested class is in the lexical scope of that class; it obeys the same rules for name binding as a static member function of that class (9.4 [class.static]) and has no special access rights to members of an enclosing class.

I believe the “no special access rights” language should be removed.

Proposed resolution (October, 2005):

This issue is resolved by the resolution of issue 372.




9. Clarification of access to base class members

Section: 11.2  [class.access.base]     Status: WP     Submitter: unknown     Date: unknown

[Moved to DR at 4/01 meeting.]

11.2 [class.access.base] paragraph 4 says:

A base class is said to be accessible if an invented public member of the base class is accessible. If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class.
Given the above, is the following well-formed?
    class D;
     
    class B
    {
     protected:
       int b1;
 
       friend void foo( D* pd );
    };
     
    class D : protected B { };
     
    void foo( D* pd )
    {
       if ( pd->b1 > 0 ); // Is 'b1' accessible?
    }
Can you access the protected member b1 of B in foo? Can you convert a D* to a B* in foo?

1st interpretation:

A public member of B is accessible within foo (since foo is a friend), therefore foo can refer to b1 and convert a D* to a B*.

2nd interpretation:

B is a protected base class of D, and a public member of B is a protected member of D and can only be accessed within members of D and friends of D. Therefore foo cannot refer to b1 and cannot convert a D* to a B*.

(See J16/99-0042 = WG21 N1218.)

Proposed Resolution (04/01):

  1. Add preceding 11.2 [class.access.base] paragraph 4:
    A base class B of N is accessible at R, if
    • an invented public member of B would be a public member of N, or
    • R occurs in a member or friend of class N, and an invented public member of B would be a private or protected member of N, or
    • R occurs in a member or friend of a class P derived from N, and an invented public member of B would be a private or protected member of P, or
    • there exists a class S such that B is a base class of S accessible at R and S is a base class of N accessible at R. [Example:
          class B {
          public:
              int m;
          };
      
          class S: private B {
              friend class N;
          };
      
          class N: private S {
              void f() {
      	    B* p = this;  // OK because class S satisfies the
      			// fourth condition above: B is a base
      			// class of N accessible in f() because
      			// B is an accessible base class of S
      			// and S is an accessible base class of N.
              }
          };
      
      end example]
  2. Delete the first sentence of 11.2 [class.access.base] paragraph 4:
    A base class is said to be accessible if an invented public member of the base class is accessible.
  3. Replace the last sentence ("A member m is accessible...") by the following:
    A member m is accessible at the point R when named in class N if
    • m as a member of N is public, or
    • m as a member of N is private, and R occurs in a member or friend of class N, or
    • m as a member of N is protected, and R occurs in a member or friend of class N, or in a member or friend of a class P derived from N, where m as a member of P is private or protected, or
    • there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B. [Example:...

The resolution for issue 207 modifies this wording slightly.




16. Access to members of indirect private base classes

Section: 11.2  [class.access.base]     Status: WP     Submitter: unknown     Date: unknown

[Moved to DR at 4/01 meeting.]

The text in 11.2 [class.access.base] paragraph 4 does not seem to handle the following cases:

    class D;
     
    class B {
    private:
        int i;
        friend class D;
    };
     
    class C : private B { };
     
    class D : private C {
        void f() {
            B::i; //1: well-formed?
            i;    //2: well-formed?
        }
    };
The member i is not a member of D and cannot be accessed in the scope of D. What is the naming class of the member i on line //1 and line //2?

Proposed Resolution (04/01): The resolution for this issue is contained in the resolution for issue 9..




207. using-declarations and protected access

Section: 11.2  [class.access.base]     Status: WP     Submitter: Jason Merrill     Date: 28 Feb 2000

[Moved to DR at 10/01 meeting.]

Consider the following example:

  class A {
  protected:
    static void f() {};
  };

  class B : A {
  public:
    using A::f;
    void g() {
      A::f();
    }
  };

The standard says in 11.2 [class.access.base] paragraph 4 that the call to A::f is ill-formed:

A member m is accessible when named in class N if

Here, m is A::f and N is A.

It seems clear to me that the third bullet should say "public, private or protected".

Steve Adamczyk:The words were written before using-declarations existed, and therefore didn't anticipate this case.

Proposed resolution (04/01):

Modify the third bullet of the third change ("A member m is accessible...") in the resolution of issue 9 to read "public, private, or protected" instead of "private or protected."




77. The definition of friend does not allow nested classes to be friends

Section: 11.4  [class.friend]     Status: WP     Submitter: Judy Ward     Date: 15 Dec 1998

[Moved to DR at 4/02 meeting.]

The definition of "friend" in 11.4 [class.friend] says:

A friend of a class is a function or class that is not a member of the class but is permitted to use the private and protected member names from the class. ...
A nested class, i.e. INNER in the example below, is a member of class OUTER. The sentence above states that it cannot be a friend. I think this is a mistake.
    class OUTER {
        class INNER;
        friend class INNER;
        class INNER {};
    };

Proposed resolution (04/01):

Change the first sentence of 11.4 [class.friend] as follows:

A friend of a class is a function or class that is not a member of the class but is allowed given permission to use the private and protected member names from the class. The name of a friend is not in the scope of the class, and the friend is not called with the member access operators (5.2.5 [expr.ref]) unless it is a member of another class. A class specifies its friends, if any, by way of friend declarations. Such declarations give special access rights to the friends, but they do not make the nominated friends members of the befriending class.



500. Access in base-specifiers of friend and nested classes

Section: 11.4  [class.friend]     Status: WP     Submitter: Andreas Hommel     Date: 25 Jan 2005

[Voted into WP at the October, 2006 meeting.]

I don't know the reason for this distinction, but it seems to be surprising that Base::A is legal and D is illegal in this example:

    class D;
    class Base 
    { 
        class A;
        class B;
        friend class D;
    }; 
    class Base::B 
    {
    };
    class Base::A : public Base::B  // OK because of issue 45
    { 
    };
    class D : public Base::B        // illegal because of 11.4p4
    { 
    };

Shouldn't this be consistent (either way)?

Notes from the April, 2005 meeting:

In discussing issue 372, the CWG decided that access in the base-specifiers of a class should be the same as for its members, and that resolution will apply to friend declarations, as well.

Proposed resolution (October, 2005):

This issue is resolved by the resolution of issue 372.




385. How does protected member check of 11.5 interact with using-declarations?

Section: 11.5  [class.protected]     Status: WP     Submitter: Vincent Korstanje     Date: 24 Sep 2002

[Voted into WP at October 2004 meeting.]

We consider it not unreasonable to do the following

  class A { 
    protected: 
    void g();
  }; 
  class B : public A { 
    public: 
      using A::g; // B::g is a public synonym for A::g 
  }; 

  class C: public A {
    void foo();
  };

  void C::foo() { 
    B b; 
    b.g(); 
  } 

However the EDG front-end does not like and gives the error

  #410-D: protected function "A::g" is not accessible through a "B" pointer or  object 
    b.g();
      ^

Steve Adamczyk: The error in this case is due to 11.5 [class.protected] of the standard, which is an additional check on top of the other access checking. When that section says "a protected nonstatic member function ... of a base class" it doesn't indicate whether the fact that there is a using-declaration is relevant. I'd say the current wording taken at face value would suggest that the error is correct -- the function is protected, even if the using-declaration for it makes it accessible as a public function. But I'm quite sure the wording in 11.5 [class.protected] was written before using-declarations were invented and has not been reviewed since for consistency with that addition.

Notes from April 2003 meeting:

We agreed that the example should be allowed.

Proposed resolution (April 2003, revised October 2003):

Change 11.5 [class.protected] paragraph 1 from

When a friend or a member function of a derived class references a protected nonstatic member function or protected nonstatic data member of a base class, an access check applies in addition to those described earlier in clause 11 [class.access]. [Footnote: This additional check does not apply to other members, e.g. static data members or enumerator member constants.] Except when forming a pointer to member (5.3.1 [expr.unary.op]), the access must be through a pointer to, reference to, or object of the derived class itself (or any class derived from that class (5.2.5 [expr.ref]). If the access is to form a pointer to member, the nested-name-specifier shall name the derived class (or any class derived from that class).

to

An additional access check beyond those described earlier in clause 11 [class.access] is applied when a nonstatic data member or nonstatic member function is a protected member of its naming class (11.2 [class.access.base]). [Footnote: This additional check does not apply to other members, e.g., static data members or enumerator member constants.] As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1 [expr.unary.op]), the nested-name-specifier shall name C or a class derived from C. All other accesses involve a (possibly implicit) object expression (5.2.5 [expr.ref]). In this case, the class of the object expression shall be C or a class derived from C.

Additional discussion (September, 2004):

Steve Adamczyk: I wonder if this wording is incorrect. Consider:

    class A {
    public:
      int p;
    };
    class B : protected A {
      // p is a protected member of B
    };
    class C : public B {
      friend void fr();
    };
    void fr() {
      B *pb = new B;
      pb->p = 1;  // Access okay?  Naming class is B, p is a protected member of B,
                  // the "C" of the issue 385 wording is C, but access is not via
                  // an object of type C or a derived class thereof.
    }

I think the formulation that the member is a protected member of its naming class is not what we want. I think we intended that the member is protected in the declaration that is found, where the declaration found might be a using-declaration.

Mike Miller: I think the proposed wording makes the access pb->p ill-formed, and I think that's the right thing to do.

First, protected inheritance of A by B means that B intends the public and protected members of A to be part of B's implementation, available to B's descendants only. (That's why there's a restriction on converting from B* to A*, to enforce B's intention on the use of members of A.) Consequently, I see no difference in access policy between your example and

    class B {
    protected:
        int p;
    };

Second, the reason we have this rule is that C's use of inherited protected members might be different from their use in a sibling class, say D. Thus members and friends of C can only use B::p in a manner consistent with C's usage, i.e., in C or derived-from-C objects. If we rewrote your example slightly,

    class D: public B { };

    void fr(B* pb) {
        pb->p = 1;
    }

    void g() {
        fr(new D);
    }

it's clear that the intent of this rule is broken — fr would be accessing B::p assuming C's policies when the object in question actually required D's policies.

(See also issues 471 and 472.)




10. Can a nested class access its own class name as a qualified name if it is a private member of the enclosing class?

Section: 11.8  [class.access.nest]     Status: WP     Submitter: Josee Lajoie     Date: unknown

[Moved to DR at 4/01 meeting.]

Paragraph 1 says: "The members of a nested class have no special access to members of an enclosing class..."

This prevents a member of a nested class from being defined outside of its class definition. i.e. Should the following be well-formed?

    class D {
        class E {
            static E* m;
        };
    };
     
    D::E* D::E::m = 1; // ill-formed
This is because the nested class does not have access to the member E in D. 11 [class.access] paragraph 5 says that access to D::E is checked with member access to class E, but unfortunately that doesn't give access to D::E. 11 [class.access] paragraph 6 covers the access for D::E::m, but it doesn't affect the D::E access. Are there any implementations that are standard compliant that support this?

Here is another example:

    class C {
        class B
        {
            C::B *t; //2 error, C::B is inaccessible
        };
    };
This causes trouble for member functions declared outside of the class member list. For example:
    class C {
        class B
        {
            B& operator= (const B&);
        };
    };
     
    C::B& C::B::operator= (const B&) { } //3
If the return type (i.e. C::B) is access checked in the scope of class B (as implied by 11 [class.access] paragraph 5) as a qualified name, then the return type is an error just like referring to C::B in the member list of class B above (i.e. //2) is ill-formed.

Proposed resolution (04/01):

The resolution for this issue is incorporated into the resolution for issue 45.




45. Access to nested classes

Section: 11.8  [class.access.nest]     Status: WP     Submitter: Daveed Vandevoorde     Date: 29 Sep 1998

[Moved to DR at 4/01 meeting.]

Example:

    #include <iostream.h>
    
    class C {  // entire body is private
        struct Parent {
            Parent() { cout << "C::Parent::Parent()\n"; }
        };
    
        struct Derived : Parent {
            Derived() { cout << "C::Derived::Derived()\n"; }
        };
    
        Derived d;
    };
    
    
    int main() {
        C c;      //  Prints message from both nested classes
        return 0;
    }
How legal/illegal is this? Paragraphs that seem to apply here are:

11 [class.access] paragraph 1:

A member of a class can be
and 11.8 [class.access.nest] paragraph 1:
The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11 [class.access] ) shall be obeyed. [...]
This makes me think that the ': Parent' part is OK by itself, but that the implicit call of 'Parent::Parent()' by 'Derived::Derived()' is not.

From Mike Miller:

I think it is completely legal, by the reasoning given in the (non-normative) 11.8 [class.access.nest] paragraph 2. The use of a private nested class as a base of another nested class is explicitly declared to be acceptable there. I think the rationale in the comments in the example ("// OK because of injection of name A in A") presupposes that public members of the base class will be public members in a (publicly-derived) derived class, regardless of the access of the base class, so the constructor invocation should be okay as well.

I can't find anything normative that explicitly says that, though.

(See also papers J16/99-0009 = WG21 N1186, J16/00-0031 = WG21 N1254, and J16/00-0045 = WG21 N1268.)

Proposed Resolution (04/01):

  1. Insert the following as a new paragraph following 11 [class.access] paragraph 1:

    A member of a class can also access all names as the class of which it is a member. A local class of a member function may access the same names that the member function itself may access. [Footnote: Access permissions are thus transitive and cumulative to nested and local classes.]
  2. Delete 11 [class.access] paragraph 6.

  3. In 11.8 [class.access.nest] paragraph 1, change

    The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (clause 11 [class.access]) shall be obeyed.

    to

    A nested class is a member and as such has the same access rights as any other member.

    Change

        B b;       // error: E::B is private
    

    to

        B b;       // Okay, E::I can access E::B
    

    Change

        p->x = i;      // error: E::x is private
    

    to

        p->x = i;      // Okay, E::I can access E::x
    
  4. Delete 11.8 [class.access.nest] paragraph 2.

(This resolution also resolves issues 8 and 10.




263. Can a constructor be declared a friend?

Section: 12.1  [class.ctor]     Status: WP     Submitter: Martin Sebor     Date: 13 Nov 2000

[Voted into WP at April 2003 meeting.]

According to 12.1 [class.ctor] paragraph 1, a declaration of a constructor has a special limited syntax, in which only function-specifiers are allowed. A friend specifier is not a function-specifier, so one interpretation is that a constructor cannot be declared in a friend declaration.

(It should also be noted, however, that neither friend nor function-specifier is part of the declarator syntax, so it's not clear that anything conclusive can be derived from the wording of 12.1 [class.ctor].)

Notes from 04/01 meeting:

The consensus of the core language working group was that it should be permitted to declare constructors as friends.

Proposed Resolution (revised October 2002):

Change paragraph 1a in 3.4.3.1 [class.qual] (added by the resolution of issue 147) as follows:

If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (clause 9 [class]), the name is instead considered to name the constructor of class C. Such a constructor name shall be used only in the declarator-id of a constructor definition declaration that appears outside of the class definition names a constructor....

Note: the above does not allow qualified names to be used for in-class declarations; see 8.3 [dcl.meaning] paragraph 1. Also note that issue 318 updates the same paragraph.

Change the example in 11.4 [class.friend], paragraph 4 as follows:

class Y {
  friend char* X::foo(int);
  friend X::X(char);   // constructors can be friends
  friend X::~X();      // destructors can be friends
  //...
};



326. Wording for definition of trivial constructor

Section: 12.1  [class.ctor]     Status: WP     Submitter: James Kanze     Date: 9 Dec 2001

[Voted into WP at October 2003 meeting.]

In 12.1 [class.ctor] paragraph 5, the standard says "A constructor is trivial if [...]", and goes on to define a trivial default constructor. Taken literally, this would mean that a copy constructor can't be trivial (contrary to 12.8 [class.copy] paragraph 6). I suggest changing this to "A default constructor is trivial if [...]". (I think the change is purely editorial.)

Proposed Resolution (revised October 2002):

Change 12.1 [class.ctor] paragraph 5-6 as follows:

A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared user-declared constructor for class X, a default constructor is implicitly declared. An implicitly-declared implicitly-declared default constructor is an inline public member of its class. A default constructor is trivial if it is an implicitly-declared default constructor and if:

Otherwise, the default constructor is non-trivial.

Change 12.4 [class.dtor] paragraphs 3-4 as follows (the main changes are removing italics):

If a class has no user-declared user-declared destructor, a destructor is declared implicitly. An implicitly-declared implicitly-declared destructor is an inline public member of its class. A destructor is trivial if it is an implicitly-declared destructor and if:

Otherwise, the destructor is non-trivial non-trivial.

In 9.5 [class.union] paragraph 1, change "trivial constructor" to "trivial default constructor".

In 12.2 [class.temporary] paragraph 3, add to the reference to 12.1 [class.ctor] a second reference, to 12.8 [class.copy].




331. Allowed copy constructor signatures

Section: 12.1  [class.ctor]     Status: WP     Submitter: Richard Smith     Date: 8 Jan 2002

[Voted into WP at October 2003 meeting.]

12.1 [class.ctor] paragraph 10 states

A copy constructor for a class X is a constructor with a first parameter of type X & or of type const X &. [Note: see 12.8 [class.copy] for more information on copy constructors.]

No mention is made of constructors with first parameters of types volatile X & or const volatile X &. This statement seems to be in contradiction with 12.8 [class.copy] paragraph 2 which states

A non-template constructor for class X is a copy constructor if its first parameter is of type X &, const X &, volatile X & or const volatile X &, ...

12.8 [class.copy] paragraph 5 also mentions the volatile versions of the copy constructor, and the comparable paragraphs for copy assignment (12.8 [class.copy] paragraphs 9 and 10) all allow volatile versions, so it seems that 12.1 [class.ctor] is at fault.

Proposed resolution (October 2002):

Change 12.1 [class.ctor] paragraph 10 from

A copy constructor for a class X is a constructor with a first parameter of type X& or of type const X&. [Note: see 12.8 [class.copy] for more information on copy constructors. ]
to (note that the dropping of italics is intentional):
A copy constructor (12.8 [class.copy]) is used to copy objects of class type.




86. Lifetime of temporaries in query expressions

Section: 12.2  [class.temporary]     Status: WP     Submitter: Steve Adamczyk     Date: Jan 1999

[Voted into WP at April, 2006 meeting.]

In 12.2 [class.temporary] paragraph 5, should binding a reference to the result of a "?" operation, each of whose branches is a temporary, extend both temporaries?

Here's an example:

    const SFileName &C = noDir ? SFileName("abc") : SFileName("bcd");

Do the temporaries created by the SFileName conversions survive the end of the full expression?

Notes from 10/00 meeting:

Other problematic examples include cases where the temporary from one branch is a base class of the temporary from the other (i.e., where the implementation must remember which type of temporary must be destroyed), or where one branch is a temporary and the other is not. Similar questions also apply to the comma operator. The sense of the core language working group was that implementations should be required to support these kinds of code.

Notes from the March 2004 meeting:

We decided that the cleanest model is one in which any "?" operation that returns a class rvalue always copies one of its operands to a temporary and returns the temporary as the result of the operation. (Note that this may involve slicing.) An implementation would be free to optimize this using the rules in 12.8 [class.copy] paragraph 15, and in fact we would expect that in many cases compilers would do such optimizations. For example, the compiler could construct both rvalues in the above example into a single temporary, and thus avoid a copy.

See also issue 446.

Proposed resolution (October, 2004):

This issue is resolved by the resolutions of issue 446.

Note (October, 2005):

This issue was overlooked when issue 446 was moved to “ready” status and was thus inadvertently omitted from the list of issues accepted as Defect Reports at the October, 2005 meeting.




124. Lifetime of temporaries in default initialization of class arrays

Section: 12.2  [class.temporary]     Status: WP     Submitter: Jack Rouse     Date: 3 June 1999

[Moved to DR at 4/01 meeting.]

Jack Rouse: 12.2 [class.temporary] states that temporary objects will normally be destroyed at the end of the full expression in which they are created. This can create some unique code generation requirements when initializing a class array with a default constructor that uses a default argument. Consider the code:

    struct T {
       int i;
       T( int );
       ~T();
    };

    struct S {
       S( int = T(0).i );
       ~S();
    };

    S* f( int n )
    {
       return new S[n];
    }
The full expression allocating the array in f(int) includes the default constructor for S. Therefore according to 1.9 [intro.execution] paragraph 14, it includes the default argument expression for S(int). So evaluation of the full expression should include evaluating the default argument "n" times and creating "n" temporaries of type T. But the destruction of the temporaries must be delayed until the end of the full expression so this requires allocating space at runtime for "n" distinct temporaries. It is unclear how these temporaries are supposed to be allocated and deallocated. They cannot readily be autos because a variable allocation is required.

I believe that many existing implementations will destroy the temporaries needed by the default constructor after each array element is initialized. But I can't find anything in the standard that allows the temporaries to be destroyed early in this case.

I think the standard should allow the early destruction of temporaries used in the default initialization of class array elements. I believe early destruction is the status quo, and I don't think the users of existing C++ compilers have been adversely impacted by it.

Proposed resolution (04/01):

The proposed resolution is contained in the proposal for issue 201.




199. Order of destruction of temporaries

Section: 12.2  [class.temporary]     Status: WP     Submitter: Alan Nash     Date: 27 J