Document number:   J16/01-0036 = WG21 N1322
Date:  12 September, 2001
Project:  Programming Language C++
Reference:  ISO/IEC IS 14882:1998(E)
Reply to:  J. Stephen Adamczyk
 jsa@edg.com


C++ Standard Core Language Defect Reports, Revision 19

Committee Version


This document contains the C++ core language issues that have been categorized as Defect Reports by the C++ Standard Committee (J16 + WG21), along with their proposed resolutions. THESE RESOLUTIONS ARE NOT YET PART OF THE INTERNATIONAL STANDARD FOR C++. They 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.


Issues with "DR" Status


173. Constraints on execution character set

Section: 2.2  lex.charset     Status: DR     Submitter: Markus Mauhart     Date: 27 Sep 1999     Drafting: Nelson

22.2.1.1.2  lib.locale.ctype.virtuals paragraph 13 states a constraint on the values of the characters representing the decimal digits in the execution character set:

for any digit character c, the expression (do_narrow( c, dfault)-'0') evaluates to the digit value of the character.
This requirement is not reflected in the description of the execution character set (2.2  lex.charset paragraph 3).

Proposed resolution (10/00):

In 2.2  lex.charset paragraph 3, after the sentence

For each basic execution character set, the values of the members shall be non-negative and distinct from one another.
insert the following:
In both the source and execution basic character sets, the value of each character after 0 in the above list of decimal digits shall be one greater than the value of the previous.



41. Clarification of lookup of names after declarator-id

Section: 3.4.1  basic.lookup.unqual     Status: DR     Submitter: Mike Miller     Date: 1 Sep 1998

From reflector message core-7838.

Footnotes 26 and 29 both use the phrase "following the function declarator" incorrectly: the function declarator includes the parameter list, but the footnotes make clear that they intend what's said to apply to names inside the parameter list. Presumably the phrase should be "following the function declarator-id."

Proposed Resolution (04/99): Change the text in 3.4.1  basic.lookup.unqual paragraph 6 from:

A name used in the definition of a function [footnote: This refers to unqualified names following the function declarator; such a name may be used as a type or as a default argument name in the parameter-declaration-clause, or may be used in the function body. end footnote] that is ...
to:
A name used in the definition of a function following the function's declarator-id [footnote: This refers to unqualified names that occur, for instance, in a type or default argument expression in the parameter-declaration-clause or used in the function body. end footnote] that is ...
Change the text in 3.4.1  basic.lookup.unqual paragraph 8 from:
A name used in the definition of a function that is a member function (9.3  class.mfct ) [footnote: That is, an unqualified name following the function declarator; such a name may be used as a type or as a default argument name in the parameter-declaration-clause, or may be used in the function body, or, if the function is a constructor, may be used in the expression of a mem-initializer. end footnote] of class X shall be ...
to:
A name used in the definition of a member function (9.3  class.mfct ) of class X following the function's declarator-id [footnote: That is, an unqualified name that occurs, for instance, in a type or default argument expression in the parameter-declaration-clause, in the function body, or in an expression of a mem-initializer in a constructor definition. end footnote] shall be ...



33. Argument dependent lookup and overloaded functions

Section: 3.4.2  basic.lookup.koenig     Status: DR     Submitter: Jason Merrill     Date: 15 Jul 1998

From reflector message core-7768.

If an argument used for lookup is the address of a group of overloaded functions, are there any associated namespaces or classes? What if it's the address of a function template?

My inclination is to say no to both.

From Mike Miller:

We discussed this on the reflector a few weeks ago. I'll leave the template case for the Core III experts, but I'd find it surprising if the overload case weren't handled as the obvious generalization of the single-function case. For a single function, the associated namespaces are those of the types used in the parameters and return type; I would expect that using an overloaded function name would simply be the union of the namespaces from the members of the overload set. That would be the simplest and most intuitive, IMHO — is there an argument for doing it differently?

Proposed Resolution (04/99): In 3.4.2  basic.lookup.koenig paragraph 2, add following the last bullet in the list of associated classes and namespaces for various argument types (not a bullet itself because overload sets and templates do not have a type):

In addition, if the argument is the name or address of a set of overloaded functions and/or function templates, its associated classes and namespaces are the union of those associated with each of the members of the set: the namespace in which the function or function template is defined and the classes and namespaces associated with its (non-dependent) parameter types and return type.



90. Should the enclosing class be an "associated class" too?

Section: 3.4.2  basic.lookup.koenig     Status: DR     Submitter: John Spicer     Date: 2 Feb 1999

From reflector message core-7952.

Section 3.4.2  basic.lookup.koenig includes the following:

Note that for a union, the enclosing class is an "associated class", but for a class type the enclosing class is not an "associated class". This results in some surprising behavior, as shown in the example below.
    struct A {
        union U {};
        friend void f(U);
    };
            
    struct B {
        struct S {};
        friend void f(S);
    };
             
    int main() { 
        A::U    u; 
        f(u);        // okay: A is an associated class
        B::S    s;
        f(s);        // error: no matching f(), B is not an associated class
    }

Certainly the enclosing class should also be an associated class for nested class types, shouldn't it?

Proposed Resolution (10/99): Change the two referenced bullets to read:

(This proposal also addresses Core issue 91.)


164. Overlap between Koenig and normal lookup

Section: 3.4.2  basic.lookup.koenig     Status: DR     Submitter: Derek Inglis     Date: 3 Sep 1999     Drafting: Spicer

The description of Koenig lookup in 3.4.2  basic.lookup.koenig paragraph 1 says,

...other namespaces not considered during the usual unqualified lookup (3.4.1  basic.lookup.unqual ) may be searched.
Does this mean that Koenig lookup does not search namespaces that were already searched during the usual unqualified lookup? The answer is academic except for the two-stage lookup during template instantiation. If a given namespace is searched in the context of the template definition, are declarations in that namespace in the instantiation context ignored during the Koenig lookup? For instance,
    void f(int);

    template <class T> void g(T t) {
        f(t);
    }

    enum E { e };

    void f(E);

    void h() {
        g(e);
    }
In this example, the call f(t) in the template function will resolve to f(E) if Koenig lookup reexamines already-searched namespaces and to f(int) if not.

Proposed Resolution (10/00):

Immediately preceding the example at the end of 3.4.2  basic.lookup.koenig paragraph 2, add the following:

[Note: the namespaces and classes associated with the argument types can include namespaces and classes already considered by the ordinary unqualified lookup.]



85. Redeclaration of member class

Section: 3.4.4  basic.lookup.elab     Status: DR     Submitter: Steve Adamczyk     Date: 25 Jan 1999     Drafting: Adamczyk

In 3.4.4  basic.lookup.elab paragraph 3, there is the example

    struct Base {
        // ...
        struct Data { /* ... */ };  // Defines nested Data
        struct Data;                // OK: Redeclares nested Data
    };
The final redeclaration is invalid according to 9.2  class.mem paragraph 1 last sentence.

Proposed resolution (10/00): Remove the line

        struct Data;                // OK: Redeclares nested Data

See also Core issue 36 and Core issue 56.




89. Object lifetime does not account for reference rebinding

Section: 3.8  basic.life     Status: DR     Submitter: AFNOR     Date: 27 Oct 1998     Drafting: Miller

From J16/98-0026 = WG21 N1169, "Proposed Defect Reports on ISO/IEC 14882, Programming Languages - C++":
A reference is rebindable. This is surprising and unnatural. This can also cause subtle optimizer bugs.

Example:

    struct T {
        int& ri;
        T (int& r) : ri (r) { }
    };
    
    void bar (T*);
    
    void foo () {
        int i;
        T x (i);
        x.ri = 3;   // the optimizer understands that this is really i = 3
        bar (&x);
        x.ri = 4;   // optimizer assumes that this writes to i, but this is incorrect
    }
    
    int gi;
    
    void bar (T* p) {
        p->~T ();
        new (p) T (gi);
    }
If we replace T& with T* const in the example then undefined behavior result and the optimizer is correct.

Proposal: make T& equivalent to T* const by extending the scope of 3.8  basic.life paragraph 9 to references.

(See also J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")

In addition, Lisa Lippincott pointed out the following example:

    void f( const bool * );
    void g();

    int main() {
       const bool *b = new const bool( false );
       f(b);
       if (*b)
          g();
    }

    void f( const bool *b ) {
       new ( const_cast<bool *>(b) ) const bool( true );
    }

The proposed wording in the paper would still permit this usage and thus prevent an optimizer from eliminating the call to g().

Proposed Resolution (10/00):

Add a new bullet to the list of restrictions in 3.8  basic.life paragraph 7, following the second bullet ("the new object is of the same type..."):




93. Missing word in 3.8 basic.life paragraph 2

Section: 3.8  basic.life     Status: DR     Submitter: Mike Miller     Date: 6 Feb 1999

From reflector message core-7956.

The text of 3.8  basic.life paragraph 2 currently reads,

The phrase "an object of type" is obviously incorrect. I believe it should read "an object of POD type." Does anyone disagree?

Proposed Resolution (10/99): As suggested.




43. Copying base classes (PODs) using memcpy

Section: 3.9  basic.types     Status: DR     Submitter: Nathan Myers     Date: 15 Sep 1998

From reflector message core-7850.

Can you use memcpy on non-member POD subobjects of non-POD objects?

In 3.9  basic.types paragraphs 2 and 3 we have:

For any complete POD object type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7  intro.memory ) making up the object can be copied into an array of char or unsigned char*. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [Example elided]
*[Footnote: By using, for example, the library functions (17.4.1.2  lib.headers ) memcpy or memmove. end footnote]
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, if the value of obj1 is copied into obj2, using the memcpy library function, obj2 shall subsequently hold the same value as obj1.
Paragraph 3 doesn't repeat the restriction of paragraph 2. Should it be assumed? Otherwise only complete POD types are copyable to an array of char and back, but scribbling over subobjects is OK. (Or perhaps a "distinct T object" is a complete object...)

Proposed Resolution (04/99): Change the text in 3.9  basic.types paragraph 2 from:

For any complete POD object type T, ...
to:
For any object (other than a base class subobject) of POD type T, ...
Change the text in 3.9  basic.types paragraph 3 from:
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2,
to:
For any POD type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base class subobject, ...



149. Accessibility and ambiguity

Section: 4.10  conv.ptr     Status: DR     Submitter: Nathan Sidwell     Date: 31 Jul 1999     Drafting: Adamczyk

The Standard uses confusing terminology when referring to accessibility in connection with ambiguity. For instance:

4.10  conv.ptr paragraph 3:

If B is an inaccessible or ambiguous base ...
5.2.7  expr.dynamic.cast paragraph 8:
... has an unambiguous public base ...
10.3  class.virtual paragraph 5:
... is an unambiguous direct or indirect base ... and is accessible ...
15.3  except.handle paragraph 3:
not involving conversions to pointers to private or protected or ambiguous classes

The phrase "unambiguous public base" is unfortunate as it could mean either "an unambiguous base not considering accessibility, which is public" or "an unambiguous base considering only the publicly accessible bases." I believe the former interpretation correct, as accessibility is applied after visibility (11  class.access paragraph 4) and ambiguity is described in terms of visibility (10.2  class.member.lookup paragraph 2).

Suggested Resolution: Use the phrases "public and unambiguous," "accessible and unambiguous," "non-public or ambiguous," or "inaccessible or ambiguous" as appropriate.

Proposed resolution (10/00):




123. Bad cross-reference

Section: 5.1  expr.prim     Status: DR     Submitter: Mike Miller     Date: 3 June 1999

From reflector message core-8092.

The cross-reference is incorrect in the first sentence after the grammar in 5.1  expr.prim paragraph 7:
A nested-name-specifier that names a class, optionally followed by the keyword template (14.8.1  temp.arg.explicit ), ...
The use of the template keyword in this context is discussed in 14.2  temp.names , not 14.8.1  temp.arg.explicit .


147. Naming the constructor

Section: 5.1  expr.prim     Status: DR     Submitter: John Spicer     Date: 21 Feb 1999     Drafting: Spicer

From paper J16/99-0010 = WG21 N1187.

5.1  expr.prim paragraph 7 says that class-name::class-name names the constructor when both class-name refer to the same class. (Note the different perspective, at least, in 12.1  class.ctor paragraph 1, in which constructors have no names and are recognized by syntactic context rather than by name.)

This formulation does not address the case of classes in which a function template is declared as a constructor, for example:

    template <class T> struct A {
        template <class T2> A(T2);
    };
    template<> template<> A<int>::A<int>(int);

Here there is an ambiguity as to whether the second template argument list is for the injected class name or for the constructor.

Suggested resolution: restate the rule as a component of name lookup. Specifically, if when doing a qualified lookup in a given class you look up a name that is the same as the name of the class, the entity found is the constructor and not the injected class name. In all other cases, the name found is the injected class name. For example:

    class B { };
    class A: public B {
        A::B ab;       // B is the inherited injected B
        A::A aa;       // Error: A::A is the constructor
    };

Without this rule some very nasty backtracking is needed. For example, if the injected class name could be qualified by its own class name, the following code would be well-formed:

    template <class T> struct A {
        template <class T2> A(T2);
        static A x;
    };
    template<> A<int>::A<int>(A<int>::x);

Here the declarator for the definition of the static data member has redundant parentheses, and it's only after seeing the declarator that the parser can know that the second A<int> is the injected class name rather than the constructor.

Proposed resolution (10/00):

In 9  class paragraph 2, change

The class-name is also inserted into the scope of the class itself. For purposes of access checking the inserted class name...

to

The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name...

Also, in 3.4.3.1  class.qual, add the following before paragraph 2:

If the nested-name-specifier nominates a class C, and the name specified after the nested-name-specifier, when looked up in C, is the injected-class-name of C (clause 9  class), the name is instead considered to name the constructor of class C. Such a constructor name shall only be used in the declarator-id of a constructor definition that appears outside of the class definition. [Example:
    struct A { A(); };
    struct B: public A { B(); };

    A::A() { }
    B::B() { }

    B::A ba;    // object of type A
    A::A a;     // error, A::A is not a type name
end example]

Also, change 3.4  basic.lookup paragraph 3 from

Because the name of a class is inserted in its class scope (clause 9  class), the name of a class is also considered a member of that class for the purposes of name hiding and lookup.

to

The injected-class-name of a class (clause 9  class) is also considered to be a member of that class for the purposes of name hiding and lookup.

(See also issue 194.)




52. Non-static members, member selection and access checking

Section: 5.2.5  expr.ref     Status: DR     Submitter: Steve Adamczyk     Date: 13 Oct 1998     Drafting: Adamczyk

5.2.5  expr.ref paragraph 4 should make it clear that when a nonstatic member is referenced in a member selection operation, the type of the left operand is implicitly cast to the naming class of the member. This allows for the detection of access and ambiguity errors on that implicit cast.

Proposed Resolution (10/00):

  1. In 11.2  class.access.base paragraph 4, remove the following from the second note:

    If the member m is accessible when named in the naming class according to the rules below, the access to m is nonetheless ill-formed if the type of p cannot be implicitly converted to type T (for example, if T is an inaccessible base class of p's class).
  2. Add the following as a new paragraph 5 of 11.2  class.access.base:

    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. [Note: this requirement is in addition to the requirement that the member be accessible as named.]
  3. In 11.2  class.access.base paragraph 4, fix a typographical error by adding the missing right parenthesis following the text

    (including cases where an implicit "this->" is added
  4. Add following the first sentence of 5.2.2  expr.call paragraph 4:

    If the function is a nonstatic member function, the "this" parameter of the function (9.3.2  class.this) shall be initialized with a pointer to the object of the call, converted as if by an explicit type conversion (5.4  expr.cast). [Note: there is no access checking on this conversion; the access checking is done as part of the (possibly implicit) class member access operator. See 11.2  class.access.base.]



53. Lvalue-to-rvalue conversion before certain static_casts

Section: 5.2.9  expr.static.cast     Status: DR     Submitter: Steve Adamczyk     Date: 13 Oct 1998     Drafting: Nelson

Section 5.2.9  expr.static.cast paragraph 6 should make it clear that when any of the "inverse of any standard conversion sequence" static_casts are done, the operand undergoes the lvalue-to-rvalue conversions first.

Proposed Resolution (10/00):

In 5.2.9  expr.static.cast paragraph 6, change

can be performed explicitly using static_cast subject to the restriction that the explicit conversion does not cast away constness (5.2.11  expr.const.cast), ...

to

can be performed explicitly using static_cast. The lvalue-to-rvalue (4.1  conv.lval), array-to-pointer (4.2  conv.array), and function-to-pointer (4.3  conv.func) conversions are applied to the operand. Such a static_cast is subject to the restriction that it does not cast away constness (5.2.11  expr.const.cast), ...



128. Casting between enum types

Section: 5.2.9  expr.static.cast     Status: DR     Submitter: Clark Nelson     Date: 10 June 1999     Drafting: Nelson

From reflector messages core-8096 through 8100.

According to 7.2  dcl.enum paragraph 9, it is permitted to convert from one enumeration type to another. However, neither 5.2.9  expr.static.cast nor 5.4  expr.cast allows this conversion.

Proposed resolution (10/00): Change the first two sentences of 5.2.9  expr.static.cast paragraph 7 to read

A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2  dcl.enum ).



137. static_cast of cv void*

Section: 5.2.9  expr.static.cast     Status: DR     Submitter: Mike Miller     Date: 13 July 1999     Drafting: Adamczyk

From reflector message core-8198.

According to 5.2.9  expr.static.cast paragraph 10,

An rvalue of type "pointer to cv void" can be explicitly converted to a pointer to object type.
No requirements are stated regarding the cv-qualification of the pointer to object type. Contrast this with the formula used in paragraphs 5, 8, and 9, where the treatment of cv-qualification is explicit, requiring that the target type be at least as cv-qualified as the source. There is an apparently general requirement on all forms of static_cast in 5.2.9  expr.static.cast paragraph 1 that it "shall not cast away constness." Assuming that this restriction applies to paragraph 10, since there is no explicit exception to the general rule, that still leaves open the question of whether one can "cast away volatility" in a conversion from volatile void* to a pointer to object type. Should 5.2.9  expr.static.cast paragraph 10 be rewritten to handle cv-qualification in the same way as paragraphs 5, 8, and 9?

Proposed resolution (10/00):

Change the first sentence of 5.2.9  expr.static.cast paragraph 10 to

An rvalue of type "pointer to cv1 void" can be converted to an rvalue of type "pointer to cv2 T", where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1.



74. Enumeration value in direct-new-declarator

Section: 5.3.4  expr.new     Status: DR     Submitter: Jason Merrill     Date: 16 Nov 1998

From reflector message core-7911.

5.3.4  expr.new paragraph 6 says:

The expression in a direct-new-declarator shall have integral type (3.9.1  basic.fundamental ) with a non-negative value.
I assume the intent was to also allow enumeral types, as we do in 5.2.1  expr.sub ?

Proposed Resolution (10/99): Replace "integral type" by "integral or enumeration type" in 5.3.4  expr.new paragraph 6.




127. Ambiguity in description of matching deallocation function

Section: 5.3.4  expr.new     Status: DR     Submitter: Alexander Schiemann     Date: 8 June 1999     Drafting: Crowl

If a placement allocation function has default arguments for all its parameters except the first, it can be called using non-placement syntax. In such a case, it is not clear whether the deallocation function to be called if the constructor terminates by throwing an expression is determined on the basis of the syntax of the new-expression (i.e., a non-placement deallocation function) or the declaration of the selected (placement) allocation function. 5.3.4  expr.new paragraph 19 indicates that the deallocation function must match the declaration of the allocation function. However, 15.2  except.ctor says that the distinction is based on whether the new-expression contains a new-placement or not.

Proposed resolution (10/00):

In 15.2  except.ctor paragraph 2, replace

If the object or array was allocated in a new-expression and the new-expression does not contain a new-placement, the deallocation function (3.7.3.2  basic.stc.dynamic.deallocation, 12.5  class.free) is called to free the storage occupied by the object; the deallocation function is chosen as specified in 5.3.4  expr.new. If the object or array was allocated in a new-expression and the new-expression contains a new-placement, the storage occupied by the object is deallocated only if an appropriate placement operator delete is found, as specified in 5.3.4  expr.new.

with

If the object or array was allocated in a new-expression, the matching deallocation function (3.7.3.2  basic.stc.dynamic.deallocation, 5.3.4  expr.new, 12.5  class.free), if any, is called to free the storage occupied by the object.



179. Function pointers and subtraction

Section: 5.7  expr.add     Status: DR     Submitter: Mike Miller     Date: Nov 1999

5.7  expr.add paragraph 8 explicitly allows subtraction of two pointers to functions:

If two pointers point to the same object or function... and the two pointers are subtracted...
However, 5.7  expr.add paragraph 2 requires that two pointers that are subtracted be pointers to an object type; function pointers are not allowed.

Being able to subtract two pointers to functions doesn't seem terribly useful, especially considering that subtracting two pointers to different functions appears to produce undefined behavior rather than simply a non-zero result, according to paragraph 6:

Unless both pointers point to elements of the same array object, or one past the last element of the array object, the behavior is undefined.

Proposed resolution (10/00):

Remove the words or function from paragraph 8.




73. Pointer equality

Section: 5.10  expr.eq     Status: DR     Submitter: Nathan Myers     Date: 13 Nov 1998     Drafting: Nelson/Miller

From reflector messages core-7890, 7895, 7896, 7904, 8101-8106.

Nathan Myers: In 5.10  expr.eq , we have:

Pointers to objects or functions of the same type (after pointer conversions) can be compared for equality. Two pointers of the same type compare equal if and only if they are both null, both point to the same object or function, or both point one past the end of the same array.
What does this say, when we have
    int i[1];
    int j[1];
about the expression (i+1 == j) ? It seems to require padding between i[0] and j[0] so that the comparison will come out false.

I think this may be a defect, in that the quoted paragraph extends operator=='s domain too far beyond operator<'s. It should permit (but not require) an off-the-end pointer to compare equal to another object, but not to any element of the same array.

Mike Miller: I think this is reading more into the statement in 5.10  expr.eq paragraph 1 than is actually there. What does it mean for a pointer to "point to" an object? I can't find anything that definitively says that i+1 cannot "point to" j[0] (although it's obviously not required to do so). If i+1 is allowed to "point to" j[0], then i+1==j is allowed to be true, and there's no defect. There are places where aliasing is forbidden, but the N+1th element of an array doesn't appear to be one of them.

To put it another way, "points to" is undefined in the Standard. The only definition I can think of that encompasses the possible ways in which a pointer can get its value (e.g., the implementation-defined conversion of an arbitrary integer value to a pointer) is that it means "having the same value representation as would be produced by applying the (builtin) & operator to an lvalue expression designating that object". In other words, if the bits are right, it doesn't matter how you produced the value, as long as you didn't perform any operations that have undefined results. The expression i+1 is not undefined, so if the bits of i+1 are the same as those of &j[0], then i+1 "points to" j[0] and i+i==j is allowed to be true.

Tom MacDonald: C9X contains the following words for the "==" operator:

Two pointers compare equal if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.
Matt Austern: I don't think there's anything wrong with saying that the result of
    int x[1];
    int y[1]; 
    std::cout << (y == x + 1) << std::endl;
is implementation defined, or even that it's undefined.

Mike Miller: A similar question could be raised about different objects that (sequentially) share the same storage. Consider the following:

    struct B {
        virtual void f();
    };
    struct D1: B { };
    struct D2: B { };
    void g() {
        B* bp1 = new D1;
        B* bp2 = new (bp1) D2;
        bp1 == bp2; // ???
    }
Section 3.8  basic.life paragraph 5 does not list this kind of comparison among the pointer operations that cause undefined behavior, so presumably the comparison is allowed. However, 5.10  expr.eq paragraph 1 describes pointer comparison in terms of "[pointing] to the same object," which bp1 and bp2 clearly do not do. How should we describe the result of this comparison?

Jason Merrill: When you consider comparing pointers to void, this seems to suggest that no two objects can have the same address, depending on your interpretation of "point to the same object." This would cripple the empty base optimization.

3.9.2  basic.compound refers to 'pointers to void or objects or functions'. In that case, 5.10  expr.eq does not allow you to compare them; it only allows comparing pointers to objects and functions.

Proposed Resolution (10/00):

(See also paper J16/00-0011 = WG21 N1234.)




188. Comma operator and rvalue conversion

Section: 5.18  expr.comma     Status: DR     Submitter: Mike Miller     Date: 20 Dec 1999     Drafting: Crowl

From reflector messages 8396-7.

Given

    char arr[100];
    sizeof(0,arr);

What does the sizeof expression return? According to 5.18  expr.comma paragraph 1, the comma operator yields an lvalue if the second argument is an lvalue. Since 4.2  conv.array paragraph 1 says that the array-to-pointer conversion yields an rvalue, it seems that sizeof should see an array type and give the answer 100. If so, the value of the sizeof expression would be different from that of the corresponding expression in C, but there is nothing in Annex C  diff to indicate that an incompatible change was intended.

Proposed resolution (10/00):

Add the following as paragraph 3 of C.1.3  diff.expr:

5.16, 5.17, 5.18

Change: The result of a conditional expression, an assignment expression, or a comma expression may be an lvalue.
Rationale: C++ is an object-oriented language, placing relatively more emphasis on lvalues. For example, functions may return lvalues.
Effect on original feature: Change to semantics of well-defined feature. Some C expressions that implicitly rely on lvalue-to-rvalue conversions will yield different results. For example,

    char arr[100];
    sizeof(0, arr)
yields 100 in C++ and sizeof(char*) in C.
Difficulty of converting: Programs must add explicit casts to the appropriate rvalue.
How widely used: Rare.




94. Inconsistencies in the descriptions of constant expressions

Section: 5.19  expr.const     Status: DR     Submitter: Mike Miller     Date: 8 Feb 1999     Drafting: Miller

From reflector messages core-7960, 7961, 7962 and 7965.

  1. According to 9.4.2  class.static.data paragraph 4, a static const integral or const enumeration data member initialized with an integral constant expression "can appear in integral constant expressions within its scope" [emphasis mine]. This means that the following is not permitted:
        struct S {
            static const int c = 5;
        };
        int a[S::c];    // error: S::c not in scope
    
    Is this restriction intentional? If so, what was the rationale for the restriction?

    Bjarne Stroustrup: I think that once you have said S::, c is in scope so that

        int a[S::c];
    
    is ok.

    Mike Miller: I'd like to think that's what it meant, but I don't believe that's what it said. According to 3.3  basic.scope paragraph 1, the scope of a name is the region "in which that name may be used as an unqualified name." You can, indeed, use a qualified name to refer to a name that is not in scope, but that only goes to reinforce my point that "S::c" is not in scope at the point where the expression containing it is used. I think the phrase "within its scope" is at best misleading and should be removed. (Unless there's a reason I'm missing for restricting the use of static member constants to their scope.)

  2. According to 5.19  expr.const paragraph 1, integral constant expressions can "involve...const variables or static data members of integral or enumeration types initialized with constant expressions." However, in 5.19  expr.const paragraph 3, arithmetic constant expressions cannot include them. This seems a rather gratuitous distinction and one likely to bite programmers trained always to use const variables instead of preprocessor definitions. Again, is there a rationale for the difference?

    As far as I can tell from 5.19  expr.const paragraph 2, "arithmetic constant expressions" (as distinct from "integral constant expressions") are used only in static initializers to distinguish between static and dynamic initialization. They include floating point types and exclude non-type template parameters, as well as the const variables and static data members.

  3. There is a minor error in 5.19  expr.const paragraph 2. The first sentence says, "Other expressions are considered constant expressions only for the purpose of non-local static object initialization." However, 6.7  stmt.dcl paragraph 4 appears to rely on the same definition dealing with the initialization of local static objects. I think that the words "non-local" should be dropped and a cross reference to 6.7  stmt.dcl added.

  4. 5.19  expr.const paragraph 4 says, "An expression that designates the address of a member or base class of a non-POD class object (clause 9) is not an address constant expression (12.7  class.cdtor )."

    I'm guessing that should be "non-static member," like the similar prohibition in 12.7  class.cdtor regarding out-of-lifetime access to members of non-POD class objects.

Proposed resolutions (10/00):

  1. Remove the phrase "within its scope" in 9.4.2  class.static.data paragraph 4.

  2. Replace 5.19  expr.const paragraph 3 with the following:
    An arithmetic constant expression shall satisfy the requirements for an integral constant expression, except that
    • floating literals need not be cast to integral or enumeration type, and
    • conversions to floating point types are permitted.
  3. This is not a defect; no change is required. The suggested wording would be more accurate, but since the effect on local initialization is unobservable the current wording is adequate.

  4. Change the referenced sentence in 5.19  expr.const paragraph 4 to "An expression that designates the address of a subobject of a non-POD class object is not an address constant expression."




227. How many scopes in an if statement?

Section: 6.4  stmt.select     Status: DR     Submitter: Marc Paterno     Date: 21 Apr 2000

The wording of 6.4  stmt.select paragraph 1 is misleading. Instead of

The substatement in a selection-statement (both substatements, in the else form of the if statement) implicitly defines a local scope (3.3  basic.scope).

it should say

... each substatement, in the else form...

As is, one is left with the impression that both "then" and "else" clauses together form a single scope.

Proposed resolution (10/00): As suggested.




69. Storage class specifiers on template declarations

Section: 7.1.1  dcl.stc     Status: DR     Submitter: Mike Ball     Date: 17 Oct 1998     Drafting: Vandevoorde

Mike Ball: I cannot find anything in the standard that tells me the meaning of a storage-class-specifier on a function template declaration. In particular, there is no indication what effect, if any, it has on the storage class of the instantiations.

There is an explicit prohibition of storage-class-specifiers on explicit specializations.

For example, if we have

    template<class T> static int foo(T) { return sizeof(T); }
does this generate static functions for all instantiations? By 7.1.1  dcl.stc the storage class applies to the name declared in the declarator, which is the template foo, not an instantiation of foo, which is named with a template-id. There is a statement in clause 14 that template names have linkage, which supports the contention that "static" applies to the template, not to instantiations.

So what does the specifier mean? Lacking a direct statement in the standard, I see the following posibilities, in my preference order.

  1. storage-class-specifiers have no meaning on template declarations, their use being subsumed by "export" (for the template name) and the unnamed namespace (for instantiations)
  2. storage-class-specifiers have no effect on the template name, but do affect the linkage of the instantiations, though this now applies linkage to template-ids, which I can find no support for. I suspect this is what was intended, though I don't remember
Of course, if anybody can find some concrete statement, that would settle it.

From John Spicer

The standard does say that a namespace scope template has external linkage unless it is a function template declared "static". It doesn't explicitly say that the linkage of the template is also the linkage of the instantiations, but I believe that is the intent. For example, a storage class is prohibited on an explicit specialization to ensure that a specialization cannot be given a different storage class than the template on which it is based.

Mike: This makes sense, but I couldn't find much support in the document. Sounds like yet another interpretation to add to the list.

John: Agreed.

The standard does not talk about the linkage of instantiations, because only "names" are considered to have linkage, and instances are not really names. So, from an implementation point of view, instances have linkage, but from a language point of view, only the template from which the instances are generated has linkage.
Mike: Which is why I think it would be cleaner to eliminate storage class specifiers entirely and rely on the unnamed namespace. There is a statement that specializations go into the namespace of the template. No big deal, it's not something it says, so we live with what's there.

John: That would mean prohibiting static function templates. I doubt those are common, but I don't really see much motivation for getting rid of them at this point.

"export" is an additional attribute that is separate from linkage, but that can only be applied to templates with external linkage.
Mike: I can't find that restriction in the standard, though there is one that templates in an unnamed namespace can't be exported. I'm pretty sure that we intended it, though.

John: I can't find it either. The "inline" case seems to be addressed, but not static. Surely this is an error as, by definition, a static template can't be used from elsewhere.

Proposed resolution (10/00):

Change the text in 14  temp paragraph 4 from:
A template name may have linkage (3.5  basic.link).
to:
A template name has linkage (3.5  basic.link). A non-member function template can have internal linkage; any other template name shall have external linkage. Entities generated from a template with internal linkage are distinct from all entities generated in other translation units.



56. Redeclaring typedefs within classes

Section: 7.1.3  dcl.typedef     Status: DR     Submitter: Steve Adamczyk     Date: 13 Oct 1998

Can a typedef redeclaration be done within a class?

    class X { 
        typedef int I; 
        typedef int I; 
    };
See also 9.2  class.mem , Core issue 36, and Core issue 85.

Proposed Resolution (10/99): Change 7.1.3  dcl.typedef paragraph 2 from "In a given scope" to "In a given non-class scope."




76. Are const volatile variables considered "constant expressions"?

Section: 7.1.5.1  dcl.type.cv     Status: DR     Submitter: Judy Ward     Date: 15 Dec 1998

The following code does not compile with the EDG compiler:

    volatile const int a = 5;
    int b[a];
The standard, 7.1.5.1  dcl.type.cv , says:
A variable of const-qualified integral or enumeration type initialized by an integral constant expression can be used in integral constant expressions.
This doesn't say it can't be const volatile-qualified, although I think that was what was intended.

Proposed Resolution (10/99): Change the referenced text in paragraph 2 of 7.1.5.1  dcl.type.cv to read:




68. Grammar does not allow "friend class A<int>;"

Section: 7.1.5.3  dcl.type.elab     Status: DR     Submitter: Mike Ball     Date: 17 Oct 1998     Drafting: Schmeiser

I can't find the answer to the following in the standard. Does anybody have a reference?

The syntax for elaborated type specifier is

Which does not allow the production

    class foo<int> // foo is a template
On the other hand, a friend declaration seems to require this production,
An elaborated-type-specifier shall be used in a friend declaration for a class.*

[Footnote: The class-key of the elaborated-type-specifier is required. —end footnote]

And in 14.5.3  temp.friend we find the example
[Example:
    template<class T> class task;
    template<class T> task<T>* preempt(task<T>*);

    template<class T> class task {
        // ...
        friend void next_time();
        friend void process(task<T>*);
        friend task<T>* preempt<T>(task<T>*);
        template<class C> friend int func(C);

        friend class task<int>;
        template<class P> friend class frd;
        // ...
    };
Is there some special dispensation somewhere to allow the syntax in this context? Is there something I've missed about elaborated-type-specifier? Is it just another bug in the standard?

An additional problem was reported via comp.std.c++: the grammar does not allow the following example:

    namespace A{
      class B{};
    };

    namespace B{
      class A{};
      class C{
	friend class ::A::B;
      };
    };

Proposed resolution (10/00):

Change the grammar in 7.1.5.3  dcl.type.elab to read

and change the forms allowed in paragraph 1 to




171. Global namespace scope

Section: 7.3  basic.namespace     Status: DR     Submitter: Greg Lutz     Date: 19 Sep 1999     Drafting: Merrill

7.3  basic.namespace paragraph 2 says:

A name declared outside all named namespaces, blocks (6.3  stmt.block ) and classes (clause 9  class ) has global namespace scope (3.3.5  basic.scope.namespace ).
But 3.3.5  basic.scope.namespace paragraph 3 says:
A name declared outside all named or unnamed namespaces (7.3  basic.namespace ), blocks (6.3  stmt.block ), function declarations (8.3.5  dcl.fct ), function definitions (8.4  dcl.fct.def ) and classes (clause 9  class ) has global namespace scope (also called global scope).
7.3  basic.namespace should evidently be changed to match the wording in 3.3.5  basic.scope.namespace — the unnamed namespace is not global scope.

Proposed resolution (10/00):

  1. Replace the first sentence of 3.3.5  basic.scope.namespace paragraph 3 with

    The outermost declarative region of a translation unit is also a namespace, called the global namespace. A name declared in the global namespace has global namespace scope (also called global scope).
  2. In the last sentence of the same paragraph, change "Names declared in the global namespace scope" to "Names with global namespace scope."

  3. Replace 7.3  basic.namespace paragraph 2 with

    The outermost declarative region of a translation unit is a namespace; see 3.3.5  basic.scope.namespace.




166. Friend declarations of template-ids

Section: 7.3.1.2  namespace.memdef     Status: DR     Submitter: John Spicer     Date: 8 Sep 1999     Drafting: Spicer

From reflector messages 8321-3.

John Spicer: I believe the standard is not clear with respect to this example:

    namespace N {
      template <class T> void f(T);
      namespace M {
        struct A {
          friend void f<int>(int);  // okay - refers to N::f
        };
      }
    }
At issue is whether the friend declaration refers to N::f, or whether it is invalid.

A note in 3.3.1  basic.scope.pdecl paragraph 6 says

friend declarations refer to functions or classes that are members of the nearest enclosing namespace ...
I believe it is intended to mean unqualified friend declarations. Certainly friend void A::B() need not refer to a member of the nearest enclosing namespace. Only when the declarator is unqualified (i.e., it is a declaration and not a reference) does this rule need to apply. The presence of an explicit template argument list requires that a previous declaration be visible and renders this a reference and not a declaration that is subject to this rule.

Mike Miller: 7.3.1.2  namespace.memdef paragraph 3 says,

When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.
On the other hand, the friend declaration would be a syntax error if f weren't declared as a template name; it would seem very strange not to find the declaration that made the friend declaration syntactically correct. However, it also seems strange to treat this case differently from ordinary functions and from templates:
    namespace N {
      template <class T> void f(T);
      void g();
      namespace M {
        struct A {
          friend void f<int>(int);               // N::f
          template <class T> friend void f(T);   // M::f
          friend void g();                       // M::g
        };
      }
    }

John Spicer: This section refers to "looking for a prior declaration". This gets back to an earlier discussion we've had about the difference between matching two declarations of the same name and doing name lookup. I would maintain that in f<int> the f is looked up using a normal lookup. In practice, this is really how it has to be done because the declaration could actually be f<int>::x.

Proposed resolution (10/00):

In 7.3.1.2  namespace.memdef paragraph 3, change

When looking for a prior declaration of a class or a function declared as a friend, scopes outside the innermost enclosing namespace scope are not considered.
to
When looking for a prior declaration of a class or a function declared as a friend, and when the name of the friend class or function is neither a qualified name nor a template-id, scopes outside the innermost enclosing namespace scope are not considered.
Also, change the example in that paragraph as follows:
    void h(int);
    template <class T> void f2(T);
    namespace A {
        class X {
            friend void f(X);       // A::f(X) is a friend
            friend void f2<>(int);  // ::f2<>(int) is a friend
    ...

(See also issues 95, 136, 138, 139, 143, and 165.)




101. Redeclaration of extern "C" names via using-declarations

Section: 7.3.3  namespace.udecl     Status: DR     Submitter: Mike Miller     Date: 10 Mar 1999

From reflector message core-7994:

Consider the following:

    extern "C" void f();
    namespace N {
        extern "C" void f();
    }
    using N::f;
According to 7.3.3  namespace.udecl paragraph 11, the using-declaration is an error:
If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, the program is ill-formed.
Based on the context (7.3.3  namespace.udecl paragraph 10 simply reiterates the requirements of 3.3  basic.scope ), one might wonder if the failure to exempt extern "C" functions was intentional or an oversight. After all, there is only one function f() involved, because it's extern "C", so ambiguity is not a reason to prohibit the using-declaration.

This also breaks the relatively strong parallel between extern "C" functions and typedefs established in our discussion of Core issue 14 in Santa Cruz. There the question was for using-directives:

    typedef unsigned int size_t;
    extern "C" int f();
    namespace N {
        typedef unsigned int size_t;
        extern "C" int f();
    }
    using namespace N;
    int i = f();        // ambiguous "f"?
    size_t x;           // ambiguous "size_t"?
We decided for both that there was no ambiguity because each pair of declarations declares the same entity. (According to 3  basic paragraph 3, a typedef name is not an entity, but a type is; thus the declarations of size_t declare the same entity "unsigned int".)

In the context of using-declarations, there is no explicit extension of the restrictions in 3.3  basic.scope paragraph 4 except as noted above for function declarations; thus the parallel scenario for a typedef is not ill-formed:

    typedef unsigned int size_t;
    namespace N {
        typedef unsigned int size_t;
    };
    using N::size_t;        // okay, both declarations
                            // refer to the same entity
I think the first sentence of 7.3.3  namespace.udecl paragraph 11 ought to be rewritten as:
If a function declaration in namespace scope or block scope has the same name and the same parameter types as a function introduced by a using-declaration, and the declarations do not declare the same function, the program is ill-formed.

Proposed Resolution (10/99): As suggested.




103. Is it extended-namespace-definition or extension-namespace-definition ?

Section: 7.3.4  namespace.udir     Status: DR     Submitter: Herb Sutter     Date: 20 Mar 1999

From reflector messages core-8001 and core-8003.

Section 7.3.4  namespace.udir paragraph 3 uses the term extended-namespace-definition three times:

If a namespace is extended by an extended-namespace-definition after a using-directive for that namespace is given, the additional members of the extended namespace and the members of namespaces nominated by using-directives in the extended-namespace-definition can be used after the extended-namespace-definition.
I think the intent is clear, but unfortunately I cannot find any other mention (or definition) of this term.

Mike Miller: True enough; in Section 7.3.1  namespace.def [the grammar] it's called an extension-namespace-definition.

Proposed Resolution (10/99): Systematically replace "extended-namespace-definition" by "extension-namespace-definition".




4. Does extern "C" affect the linkage of function names with internal linkage?

Section: 7.5  dcl.link     Status: DR     Submitter: Mike Anderson     Date: unknown     Drafting: Gibbons/Adamczyk

(Previously numbered 864.)

7.5  dcl.link paragraph 6 says the following:

Does this apply to static functions as well? For example, is the following well-formed?
        extern "C" {
            static void f(int) {}
            static void f(float) {}
        };
Can a function with internal linkage "have C linkage" at all (assuming that phrase means "has extern "C" linkage"), for how can a function be extern "C" if it's not extern? The function type can have extern "C" linkage — but I think that's independent of the linkage of the function name. It should be perfectly reasonable to say, in the example above, that extern "C" applies only to the types of f(int) and f(float), not to the function names, and that the rule in 7.5  dcl.link paragraph 6 doesn't apply.

Suggested resolution: The extern "C" linkage specification applies only to the type of functions with internal linkage, and therefore some of the rules that have to do with name overloading don't apply.

Proposed Resolution:

The intent is to distingush implicit linkage from explicit linkage for both name linkage and language (function type) linkage. (It might be more clear to use the terms name linkage and type linkage to distinguish these concepts. A function can have a name with one kind of linkage and a type with a different kind of linkage. The function itself has no linkage: it has no name, only the declaration has a name. This becomes more obvious when you consider function pointers.)

The tentatively agreed proposal is to apply implicit linkage to names declared in brace-enclosed linkage specifications and to non-top-level names declared in simple linkage specifications; and to apply explicit linkage to top-level names declared in simple linkage specifications.

The language linkage of any function type formed through a function declarator is that of the nearest enclosing linkage-specification. For purposes of determining whether the declaration of a namespace-scope name matches a previous declaration, the language linkage portion of the type of a function declaration (that is, the language linkage of the function itself, not its parameters, return type or exception specification) is ignored.

For a linkage-specification using braces, i.e.

extern string-literal { declaration-seqopt }
the linkage of any declaration of a namespace-scope name (including local externs) which is not contained in a nested linkage-specification, is not declared to have no linkage (static), and does not match a previous declaration is given the linkage specified in the string-literal. The language linkage of the type of any function declaration of a namespace-scope name (including local externs) which is not contained in a nested linkage-specification and which is declared with function declarator syntax is the same as that of a matching previous declaration, if any, else is specified by string-literal.

For a linkage-specification without braces, i.e.

extern string-literal declaration

the linkage of the names declared in the top-level declarators of declaration is specified by string-literal; if this conflicts with the linkage of any matching previous declarations, the program is ill-formed. The language linkage of the type of any top-level function declarator is specified by string-literal; if this conflicts with the language linkage of the type of any matching previous function declarations, the program is ill-formed. The effect of the linkage-specification on other (non top-level) names declared in declaration is the same as that of the brace-enclosed form.

[The following discussion is from messages 8722 and 8724.]

Bill Gibbons: In particular, these should be well-formed:

    extern "C" void f(void (*fp)());   // parameter type is pointer to
                                       // function with C language linkage
    extern "C++" void g(void (*fp)()); // parameter type is pointer to
                                       // function with C++ language linkage

    extern "C++" {                     // well-formed: the linkage of "f"
        void f(void(*fp)());           // and the function type used in the
    }                                  // parameter still "C"

    extern "C" {                       // well-formed: the linkage of "g"
        void g(void(*fp)());           // and the function type used in the
    }                                  // parameter still "C++"

but these should not:

    extern "C++" void f(void(*fp)());  // error - linkage of "f" does not
                                       // match previous declaration
                                       // (linkage of function type used in
                                       // parameter is still "C" and is not
                                       // by itself ill-formed)
    extern "C" void g(void(*fp)());    // error - linkage of "g" does not
                                       // match previous declaration
                                       // (linkage of function type used in
                                       // parameter is still "C++" and is not
                                       // by itself ill-formed)

That is, non-top-level declarators get their linkage from matching declarations, if any, else from the nearest enclosing linkage specification. (As already described, top-level declarators in a brace-enclosed linkage specification get the linkage from matching declarations, if any, else from the linkage specifcation; while top-level declarators in direct linkage specifications get their linkage from that specification.)

Mike Miller: This is a pretty significant change from the current specification, which treats the two forms of language linkage similarly for most purposes. I don't understand why it's desirable to expand the differences.

It seems very unintuitive to me that you could have a top-level declaration in an extern "C" block that would not receive "C" linkage.

In the current standard, the statement in 7.5  dcl.link paragraph 4 that

the specified language linkage applies to the function types of all function declarators, function names, and variable names introduced by the declaration(s)

applies to both forms. I would thus expect that in

    extern "C" void f(void(*)());
    extern "C++" {
        void f(void(*)());
    }
    extern "C++" f(void(*)());

both "C++" declarations would be well-formed, declaring an overloaded version of f that takes a pointer to a "C++" function as a parameter. I wouldn't expect that either declaration would be a redeclaration (valid or invalid) of the "C" version of f.

Bill Gibbons: The potential difficulty is the matching process and the handling of deliberate overloading based on language linkage. In the above examples, how are these two declarations matched:

    extern "C" void f(void (*fp1)());

    extern "C++" {
        void f(void(*fp2)());
    }

given that the linkage that is part of fp1 is "C" while the linkage (prior to the matching process) that is part of fp2 is "C++"?

The proposal is that the linkage which is part of the parameter type is not determined until after the match is attempted. This almost always correct because you can't overload "C" and "C++" functions; so if the function names match, it is likely that the declarations are supposed to be the same.

Mike Miller: This seems like more trouble than it's worth. This comparison of function types ignoring linkage specifications is, as far as I know, not found anywhere in the current standard. Why do we need to invent it?

Bill Gibbons: It is possible to construct pathological cases where this fails, e.g.

    extern "C" typedef void (*PFC)();  // pointer to "C" linkage function
    void f(PFC);         // parameter is pointer to "C" function
    void f(void (*)());  // matching declaration or overload based on
                         // difference in linkage type?

It is reasonable to require explicit typedefs in this case so that in the above example the second function declaration gets its parameter type function linkage from the first function declaration.

(In fact, I think you can't get into this situation without having already used typedefs to declare different language linkage for the top-level and parameter linkages.)

For example, if the intent is to overload based on linkage a typedef is needed:

    extern "C" typedef void (*PFC)();  // pointer to "C" linkage function
    void f(PFC);              // parameter is pointer to "C" function
    typedef void (*PFCPP)();  // pointer to "C++" linkage function
    void f(PFCPP);            // parameter is pointer to "C++" function

In this case the two function declarations refer to different functions.

Mike Miller: This seems pretty strange to me. I think it would be simpler to determine the type of the parameter based on the containing linkage specification (implicitly "C++") and require a typedef if the user wants to override the default behavior. For example:

    extern "C" {
        typedef void (*PFC)();    // pointer to "C" function
        void f(void(*)());        // takes pointer to "C" function
    }

    void f(void(*)());            // new overload of "f", taking
                                  // pointer to "C++" function

    void f(PFC);                  // redeclare extern "C" version

Notes from 04/00 meeting:

The following changes were tentatively approved, but because they do not completely implement the proposal above the issue is being kept for the moment in "drafting" status.

Notes from 10/00 meeting:

After further discussion, the core language working group determined that the more extensive proposal described above is not needed and that the following changes are sufficient.

Proposed resolution (04/01):

  1. Change the first sentence of 7.5  dcl.link paragraph 1 from

    All function types, function names, and variable names have a language linkage.

    to

    All function types, function names with external linkage, and variable names with external linkage have a language linkage.
  2. Change the following sentence of 7.5  dcl.link paragraph 4:
    In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names, and variable names introduced by the declaration(s).

    to

    In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification.
  3. Add at the end of the final example on 7.5  dcl.link paragraph 4:

        extern "C" {
          static void f4();    // the name of the function f4 has
                               // internal linkage (not C language
                               // linkage) and the function's type
                               // has C language linkage
        }
        extern "C" void f5() {
          extern void f4();    // Okay -- name linkage (internal)
                               // and function type linkage (C
                               // language linkage) gotten from
                               // previous declaration.
        }
        extern void f4();      // Okay -- name linkage (internal)
                               // and function type linkage (C
                               // language linkage) gotten from
                               // previous declaration.
        void f6() {
          extern void f4();    // Okay -- name linkage (internal)
                               // and function type linkage (C
                               // language linkage) gotten from
                               // previous declaration.
        }
    
  4. Change 7.5  dcl.link paragraph 7 from

    Except for functions with internal linkage, a function first declared in a linkage-specification behaves as a function with external linkage. [Example:

        extern "C" double f();
        static double f();     // error
    

    is ill-formed (7.1.1  dcl.stc). ] The form of linkage-specification that contains a braced-enclosed declaration-seq does not affect whether the contained declarations are definitions or not (3.1  basic.def); the form of linkage-specification directly containing a single declaration is treated as an extern specifier (7.1.1  dcl.stc) for the purpose of determining whether the contained declaration is a definition. [Example:

        extern "C" int i;      // declaration
        extern "C" {
    	  int i;           // definition
        }
    

    end example] A linkage-specification directly containing a single declaration shall not specify a storage class. [Example:

        extern "C" static void f(); // error
    

    end example]

    to

    A declaration directly contained in a linkage-specification is treated as if it contains the extern specifier (7.1.1  dcl.stc) for the purpose of determining the linkage of the declared name and whether it is a definition. Such a declaration shall not specify a storage class. [Example:
        extern "C" double f();
        static double f();     // error
        extern "C" int i;      // declaration
        extern "C" {
    	    int i;         // definition
        }
        extern "C" static void g(); // error
    

    end example]




29. Linkage of locally declared functions

Section: 7.5  dcl.link     Status: DR     Submitter: Mike Ball     Date: 19 Mar 1998     Drafting: Gibbons/Adamczyk

From reflector message core-7714.

Consider the following:

    extern "C" void foo()
    {
        extern void bar();
        bar();
    }
Does "bar()" have "C" language linkage?

The ARM is explicit and says

A linkage-specification for a function also applies to functions and objects declared within it.
The DIS says
In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names, and variable names introduced by the declaration(s).
Is the body of a function definition part of the declaration?

From Mike Miller:

Yes: from 7  dcl.dcl paragraph 1,

and 8.4  dcl.fct.def paragraph 1: At least that's how I'd read it.

From Dag Brück:

Consider the following where extern "C" has been moved to a separate declaration:

    extern "C" void foo();
    
    void foo() { extern void bar(); bar(); }
I think the ARM wording could possibly be interpreted such that bar() has "C" linkage in my example, but not the DIS wording.

As a side note, I have always wanted to think that placing extern "C" on a function definition or a separate declaration would produce identical programs.

Proposed Resolution (04/01):

See the proposed resolution for Core issue 4, which covers this case.

The ODR should also be checked to see whether it addresses name and type linkage.




40. Syntax of declarator-id

Section: 8.3  dcl.meaning     Status: DR     Submitter: Mike Miller     Date: 01 Sep 1998

(From J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")

There are two sub-issues. The first concerns the statement in 8.3  dcl.meaning paragraph 1,

The id-expression of a declarator-id shall be a simple identifier except for the declaration of some special functions (12.3  class.conv , 12.4  class.dtor , 13.5  over.oper ) and for the declaration of template specializations or partial specializations (14.7  temp.spec ).
The second sub-issue is regarding another statement in the same paragraph:
A declarator-id shall not be qualified except for the definition of a member function (9.3  class.mfct ) or static data member (9.4  class.static ) or nested class (9.7  class.nest ) outside of its class, the definition or explicit instantiation of a function, variable or class member of a namespace outside of its namespace, or...
Analysis

The problem in the first sub-issue is that the wrong syntactic non-terminal is mentioned. The relevant portions of the grammar are:

The exceptions in the citation from 8.3  dcl.meaning paragraph 1 are all the non-identifier cases of unqualified-id: 12.3  class.conv is for conversion-function-ids, 12.4  class.dtor is for destructors, 13.5  over.oper is for overloaded operators, and 14.7  temp.spec is for template-ids. If taken literally, this sentence would exclude all qualified-ids, which it obviously is not intended to do. Instead, the apparent intent is something along the lines of
If an unqualified-id is used as the id-expression of a declarator-id, it shall be a simple identifier except...
However, it does not appear that this restriction has any meaning; all of the possible cases of unqualified-ids are represented in the list of exceptions! Rather than recasting the sentence into a correct but useless form, it would be better to remove it altogether.

The second sub-issue deals with the conditions under which a qualified-id can be used in a declarator, including "the definition of a...nested class" and "the definition or explicit instantiation of a...class member of a namespace." However, the name in a class definition is not part of a declarator; these constructs do not belong in a list of declarator contexts.

Proposed Resolution for sub-issue 1 (04/99):

The suggested resolution for the first sub-issue overlooked the fact that the existing wording has the additional effect of prohibiting the use of the non-identifier syntax for declaring other than the listed entities. Thus the proposed wording for the first sub-issue is:

Change 8.3  dcl.meaning paragraph 1 from:

The id-expression of a declarator-id shall be a simple identifier except...
to:
An unqualified-id occurring in a declarator-id shall be a simple identifier except...

Proposed Resolution for sub-issue 2 (10/99):

Change 8.3  dcl.meaning paragraph 1 from:

A declarator-id shall not be qualified except for the definition of a member function (9.3  class.mfct ) or static data member (9.4  class.static ) or nested class (9.7  class.nest ) outside of its class, the definition or explicit instantiation of a function, variable or class member of a namespace outside of its namespace, or...
to
A declarator-id shall not be qualified except for the definition of a member function (9.3  class.mfct ) or static data member (9.4  class.static ) outside of its class, the definition or explicit instantiation of a function or variable member of a namespace outside of its namespace, or...



159. Namespace qualification in declarators

Section: 8.3  dcl.meaning     Status: DR     Submitter: John Spicer     Date: 23 Aug 1999     Drafting: Vandevoorde

8.3  dcl.meaning paragraph 1 says:

In the qualified declarator-id for a class or namespace member definition that appears outside of the member's class or namespace, the nested-name-specifier shall not name any of the namespaces that enclose the member's definition.
This results in the following behavior:
    namespace N {
        namespace M {
            void f();
            void g();
        }
        void M::f(){}     // okay
        void N::M::g(){}  // error
    }
I was very surprised when this rule was pointed out to me. The change appears to have been introduced around the time of the first Santa Cruz meeting, but I don't recall discussion of it and could not find a motion related to it.

Regardless of where it came from, I also can't understand why it is there. Certainly it shouldn't matter how you name a given class or namespace.

For example, the standard permits:

    namespace N {
        namespace M {
            void f();
            void g();
        }
        namespace X = M;
        namespace Y = N::M;
        void X::f(){}  // okay
        void Y::g(){}  // okay
    }
So, it is okay to use an alias for N::M, but not to use N::M directly. Note that it is okay to use N::M in any other context at this point in the program (i.e., the rule is a specific restriction on declarator names, not a general rule on the use of qualified names).

Does anyone recall the intent of this rule or any rationale for its existence?

Notes from 04/00 meeting:

There was some question as to whether this issue actually constituted a defect in the Standard. John Spicer suggested that machine-generated source code would be likely to run afoul of this prohibition. Francis Glassborow expressed support for a rule that would allow full qualification, or qualification relative to the namespace containing the definition, but not qualification relative to a containing namespace. There was no consensus for moving forward with a DR at this point, so the issue was left in "review" status.

Proposed resolution (10/00):

Remove the last sentence of 8.3  dcl.meaning paragraph 1 (cited above) and the example that follows.




135. Class type in in-class member function definitions

Section: 8.3.5  dcl.fct     Status: DR     Submitter: Gabriel Netterdag     Date: 1 July 1999     Drafting: Adamczyk

From reflector messages 8167, 8170-76.

3.2  basic.def.odr paragraph 4 and 8.3.5  dcl.fct paragraph 6 indicate that the return type and parameter types must be complete in a function definition. However, when 9.2  class.mem paragraph 2 lists the contexts in a class member-specification in which the class is considered complete, the return type and parameter types of a member function defined in the class definition are not included. It thus appears that the following example is ill-formed:

    struct S {
        S f() { return S(); }    // error: incomplete return type
        void g(S) { }            // error: incomplete parameter type
    };
Jack Rouse: I suggest supplementing the text in 8.3.5p6 with something like:
The type of a parameter or the return type for a function definition shall not be an incomplete class type unless the function definition is nested in the member-specification for that class (including definitions in nested classes defined within the class).

Proposed resolution (10/00): Replace the last sentence of 8.3.5  dcl.fct paragraph 6 with

The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).



1. What if two using-declarations refer to the same function but the declarations introduce different default-arguments?

Section: 8.3.6  dcl.fct.default     Status: DR     Submitter: Bill Gibbons     Date: unknown     Drafting: Vandevoorde

(Previously numbered 689.)

3.3  basic.scope paragraph 4 says:

Given a set of declarations in a single declarative region, each of which specifies the same unqualified name,
8.3.6  dcl.fct.default paragraph 9 says:
When a declaration of a function is introduced by way of a using-declaration (7.3.3  namespace.udecl), any default argument information associated with the declaration is imported as well.
This is not really clear regarding what happens in the following case:
    namespace A {
            extern "C" void f(int = 5);
    }
    namespace B {
            extern "C" void f(int = 7);
    }
     
    using A::f;
    using B::f;
     
    f(); // ???
Proposed resolution (10/00):

Add the following at the end of 13.3.3  over.match.best:

If the best viable function resolves to a function for which multiple declarations were found, and if at least two of these declarations — or the declarations they refer to in the case of using-declarations — specify a default argument that made the function viable, the program is ill-formed. [Example:
    namespace A {
       extern "C" void f(int = 5);
    }
    namespace B {
       extern "C" void f(int = 5);
    }

    using A::f;
    using B::f;

    void use() {
       f(3);       // OK, default argument was not used for viability
       f();        // Error: found default argument twice
    }

  —end example]




65. Typo in default argument example

Section: 8.3.6  dcl.fct.default     Status: DR     Submitter: Mike Miller     Date: 6 Oct 1998

Proposed Resolution (04/99): Change the text in the example of section 8.3.6  dcl.fct.default paragraph 5 from:

... g will be called with the value f(1).
to:
... g will be called with the value f(2).



217. Default arguments for non-template member functions of class templates

Section: 8.3.6  dcl.fct.default     Status: DR     Submitter: Martin Sebor     Date: 22 Mar 2000     Drafting: Spicer

From reflector messages 8602-4.

According to 8.3.6  dcl.fct.default paragraphs 4 and 6,

For non-template functions, default arguments can be added in later declarations of a function in the same scope.

The default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition.

This would appear to allow the following example, in which a default argument is added to a non-template member function of a class template:

    template <class T>
    struct S
    {
	void foo (int);
    };

    template <class T>
    void S<T>::foo (int = 0) { }

John Spicer: The wording "non-template functions" is somewhat unclear with respect to member functions of class templates, but I know that this was intended to include them because it originates from issue 3.13 of the template issues list that I maintained for several years.

Having said that, the rationale for this restriction has since been made obsolete, so this could (in theory) be changed in the standard if it is problematic for users.

(See also issue 205.)

Proposed resolution (10/00):

In 8.3.6  dcl.fct.default paragraph 6, replace

The default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition.

with

Except for member functions of class templates, the default arguments in a member function definition that appears outside of the class definition are added to the set of default arguments provided by the member function declaration in the class definition. Default arguments for a member function of a class template must be specified on the initial declaration of the member function within the class template.



5. CV-qualifiers and type conversions

Section: 8.5  dcl.init     Status: DR     Submitter: Josee Lajoie     Date: unknown     Drafting: Adamczyk

(Previously numbered 866.)

The description of copy-initialization in 8.5  dcl.init paragraph 14 says:

Should "destination type" in this last bullet refer to "cv-unqualified destination type" to make it clear that the destination type excludes any cv-qualifiers? This would make it clearer that the following example is well-formed:
     struct A {
       A(A&);
     };
     struct B : A { };
     
     struct C {
       operator B&();
     };
     
     C c;
     const A a = c; // allowed?

The temporary created with the conversion function is an lvalue of type B. If the temporary must have the cv-qualifiers of the destination type (i.e. const) then the copy-constructor for A cannot be called to create the object of type A from the lvalue of type const B. If the temporary has the cv-qualifiers of the result type of the conversion function, then the copy-constructor for A can be called to create the object of type A from the lvalue of type const B. This last outcome seems more appropriate.

Steve Adamczyk: (See message 8897.)

Because of late changes to this area, the relevant text is now the third sub-bullet of the fourth bullet of 8.5  dcl.init paragraph 14:

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated... The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the destination type. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.

The issue still remains whether the wording should refer to "the cv-unqualified version of the destination type." I think it should.

Notes from 10/00 meeting:

The original example does not illustrate the remaining problem. The following example does:

    struct C { };
    C c;
    struct A {
        A(const A&);
        A(const C&);
    };
    const volatile A a = c;    // Okay

Proposed Resolution (04/01):

In 8.5  dcl.init, paragraph 14, bullet 4, sub-bullet 3, change

if the function is a constructor, the call initializes a temporary of the destination type.

to

if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type.



35. Definition of default-initialization

Section: 8.5  dcl.init     Status: DR     Submitter: Andrew Koenig     Date: 29 Jul 1998

From reflector message core-7780.

Given:

    struct S1 {
        int x;
    };
    
    struct S2 {
        int x;
        double y;
    };
    
    struct S3 {
        int x;
        double y;
        string s;
    };
Once upon a time, we went through a fairly protracted discussion to ensure that S1().x would be guaranteed to be 0. Note that if we declare
    void f()
    {
        S1 s1;
    
        // ...
    }
there is no guarantee of the value of s1.x, and that is intentional. But S1().x is different, because S1() is an rvalue, and unless all of its members are defined, the effect of copying it is undefined.

Similarly, S2().x and S2().y are also defined to be equal to zero, and here it really matters for many implementations, because if S2().y is just a bunch of random bits, it is entirely possible that trying to copy S2().y will yield a floating-point trap.

However, rather to my surprise, the standard does not define the value of S3().x or S3().y, because S3 is not a POD. It does define S3().s (by running the string constructor), but once a structure is no longer a POD, the values of uninitialized members are no longer guaranteed in expressions of the form T().

In my opinion, this definition is a mistake, and the committee's intention was to zero-initialize all members that do not have an explicitly defined constructor, whether or not the class is a POD.

See core reflector messages 7780, 7782, 7785-7787, 7789-7791, 7796 and 7798-7805 for a spirited discussion of this topic. See also paper J16/99-0014 = WG21 N1191.

[Note: this issue is resolved by the resolution of issue 178.]




78. Section 8.5 paragraph 9 should state it only applies to non-static objects

Section: 8.5  dcl.init     Status: DR     Submitter: Judy Ward     Date: 15 Dec 1998     Drafting: Adamczyk

Paragraph 9 of 8.5  dcl.init says:

If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for an object, the object and its subobjects, if any, have an indeterminate initial value; if the object or any of its subobjects are of const-qualified type, the program is ill-formed.
It should be made clear that this paragraph does not apply to static objects.

Proposed resolution (10/00): In 8.5  dcl.init paragraph 9, replace

Otherwise, if no initializer is specified for an object..."
with
Otherwise, if no initializer is specified for a non-static object...



151. Terminology of zero-initialization

Section: 8.5  dcl.init     Status: DR     Submitter: Valentin Bonnard     Date: 4 August 1999     Drafting: Nelson

In 3.6.2  basic.start.init paragraph 1 and 8.5  dcl.init paragraphs 5 and 6, the terms "memory" and "storage" are used in connection with zero-initialization. This is inaccurate; it is the variables that are zero-initialized, not the storage. (An all-zero bit pattern in the storage may, in fact, not correspond to the representation of zero converted to the appropriate type, and it is the latter that is being described.)

Suggested resolution: remove the words "storage" and "memory" in these contexts.

Proposed resolution (10/00):

Delete the words "The storage for" from the first sentence of 3.6.2  basic.start.init paragraph 1.

[Note: Revised wording in 8.5  dcl.init relating to this issue is also found in issue 178.]




178. More on value-initialization

Section: 8.5  dcl.init     Status: DR     Submitter: Andrew Koenig     Date: 25 Oct 1999     Drafting: Koenig

When the Committee considered issue 35, another context in which value initialization might be relevant was overlooked: mem-initializers. It would seem reasonable that if T() as an expression invokes value initialization, that the same syntactic construct in a mem-initializer-list would do the same, and the usefulness of value initialization in that context is at least as great as the standalone case.

Proposed resolution (10/00):

[Note: this resolution supersedes the resolution to issue 35.]

In 5.2.3  expr.type.conv paragraph 2, replace "whose value is determined by default-initialization" by "which is value-initialized".

In 5.3.4  expr.new paragraph 15,

Replace 8.5  dcl.init paragraph 5 by:

To zero-initialize an object of type T means:

To default-initialize an object of type T means:

To value-initialize an object of type T means:

A program that calls for default-initialization of an entity of reference type is ill-formed. If T is a cv-qualified type, the cv-unqualified version of T is used for these definitions of zero-initialization, default-initialization, and value-initialization.

In 8.5  dcl.init paragraph 6, change "The memory occupied by any" to "Every".

In 8.5  dcl.init paragraph 7, replace "default-initialized" by "value-initialized".

In 8.5.1  dcl.init.aggr paragraph 7, replace "default-initialized" by "value-initialized".

In 12.3.1  class.conv.ctor paragraph 2, insert "or value-initialization" after the first occurrence of "default-initialization".

In 12.6  class.init paragraph 1, replace the note by "The object is default-initialized if there is no initializer, or value-initialized if the initializer is ()" [i.e., replace the non-normative note by different, normative text].

In 12.6.1  class.expl.init paragraph 2, replace "default-initialized" by "value-initialized".

In 12.6.2  class.base.init paragraph 3, replace "default-initialized" by "value-initialized" in the first bulleted item.

In 12.6.2  class.base.init paragraph 4, replace "default-initialized, nor initialized" by "default-initialized, nor value-initialized, nor assigned".




163. Description of subaggregate initializer

Section: 8.5.1  dcl.init.aggr     Status: DR     Submitter: Mike Miller     Date: 12 Aug 1999

8.5.1  dcl.init.aggr paragraph 2 says,

When an aggregate is initialized the initializer can be an initializer-clause consisting of a brace-enclosed, comma-separated list of initializers for the members of the aggregate.
Neither of these uses of the syntactic nonterminal initializer corresponds to the grammar:

Proposed resolution (10/99): replace the quoted words with:

When an aggregate is initialized the initializer can contain an initializer-clause consisting of a brace-enclosed, comma-separated list of initializer-clauses for the members of the aggregate.



148. POD classes and pointers to members

Section: class     Status: DR     Submitter: Nathan Sidwell     Date: 31 Jul 1999     Priority: 3

3.9  basic.types paragraph 10 defines pointer to member types to be scalar types. It also defines scalar types to be one of the POD types.

class paragraph 4 defines a POD struct as an aggregate class with no non-static data members of type pointer to member.

It seems contradictory that a type can be POD, yet a class containing that type is non-POD.

Suggested resolution: Alter 9  class paragraph 4 to allow pointer to member objects as non-static data members of POD class.

Proposed resolution (10/00):

In 9  class paragraph 4, remove all occurrences of "pointer to member."




176. Name injection and templates

Section: class     Status: DR     Submitter: John Spicer     Date: 21 February 1999     Priority: 1     Drafting: Spicer

There is some controversy about whether class name injection applies to class templates. If it does apply, what is injected? Is a class name injected or is the thing that is injected actually a template?

Clause 9  class paragraph 2 says,

The class-name is also inserted into the scope of the class itself.
In general, clause 9 applies to both classes and class templates, so I would take this to mean that class name imjection does indeed apply to class templates. One problem with this is that clause 9 uses the syntactic term class-name, which I would take to imply that the inserted name is always a class. This is clearly unacceptable for class templates as it makes the template itself unusable from with the template. For example:
    template <class T> struct A {
        A<T*> ptr;    // Invalid: A refers to a class
    };

Clearly the injected name must be usable as both a class and a class template. This kind of magic already exists in the standard. In 14.6.1  temp.local it says,

Within the scope of a class template, when the name of the template is neither qualified nor followed by <, it is equivalent to the name of the template followed by the template-parameters enclosed in <>.

The proposal here is that we clarify that name injection does indeed apply to class templates, and that it is the injected name that has the special property of being usable as both a class and a template name (as described in 14.6.1  temp.local ). This would eliminate the need for special wording regarding the qualification of the name, but would achieve the same result. This would also make this "special" name available to a derived class of a class template — something which is necessary if the benefits of class name injection are to be made uniformly available for class templates, too.

    template <class T> struct Base {
        Base* p;
        Base<T*>* p2;
        ::Base* p3;    // Error: only injected name usable as class
    };

    template <class T> struct Derived: public Base<T> {
        Base* p;    // Now okay
        Base<T*>* p2;    // Still okay
        Derived::Base* p3;    // Now okay
Note that by giving the special attribute of being usable as both a class and a template to the injected name it is now clear where this attribute can and cannot be used.

(See paper J16/99-0010 = WG21 N1187.)

Proposed resolution (10/00):

[Note: these changes depend on the resolution for issue 147.]

Replace 14.6.1  temp.local paragraphs 1 and 2 with the following:

Like normal (non-template) classes, class templates have an injected-class-name (clause 9  class). The injected-class-name can be used with or without a template-argument-list. When it is used without a template-argument-list, it is equivalent to the injected-class-name followed by the template-parameters of the class template enclosed in <>. When it is used with a template-argument-list, it refers to the specified class template specialization, which could be the current specialization or another specialization.

Within the scope of a class template specialization or partial specialization, when the injected-class-name is not followed by a <, it is equivalent to the injected-class-name followed by the template-arguments of the class template specialization or partial specialization enclosed in <>. [Example:

    template<class T> class Y;
    template<> class Y<int> {
        Y* p;          // meaning Y<int>
        Y<char>* q;    // meaning Y<char>
    };

end example]

The injected-class-name of a class template or class template specialization can be used either with or without a template-argument-list wherever it is in scope. [Example:

    template <class T> struct Base {
        Base* p;
    };

    template <class T> struct Derived: public Base<T> {
        typename Derived::Base* p;  // meaning Derived::Base<T>
    };

end example]

A lookup that finds an injected-class-name (10.2  class.member.lookup) can result in an ambiguity in certain cases (for example, if it is found in more than one base class). If all of the injected-class-names that are found refer to specializations of the same class template, and if the name is followed by a template-argument-list, the reference refers to the class template itself and not a specialization thereof, and is not ambiguous. [Example:

    template <class T> struct Base { };
    template <class T> struct Derived: Base<int>, Base<char> {
        typename Derived::Base b;            // error: ambiguous
        typename Derived::Base<double> d;    // OK
    };

end example]

When the normal name of the template (i.e., the name from the enclosing scope, not the injected-class-name) is used without a template-argument-list, it refers to the class template itself and not a specialization of the template. [Example:

    template <class T> class X {
        X* p;         // meaning X<T>
        X<T>* p2;
        X<int>* p3;
        ::X* p4;      // error: missing template argument list
                      // ::X does not refer to the injected-class-name
    };

end example]




75. In-class initialized members must be const

Section: 9.2  class.mem     Status: DR     Submitter: John Wiegley     Date: 29 Dec 1998

From reflector message core-7936.

The standard says, in 9.2  class.mem paragraph 4:

A member-declarator can contain a constant-initializer only if it declares a static member (9.4  class.static ) of integral or enumeration type, see 9.4.2  class.static.data .
But later, in the section on static class data member initialization, 9.4.2  class.static.data paragraph 4, it says:
If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19  expr.const ). In that case, the member can appear in integral constant expressions within its scope.
The first paragraph should be modified to make it clear that it is not possible to initialize a static data member in-line with a constant-initializer if that data member is of integral (or enumeration) type, and yet not const.

Proposed Resolution (10/99): Change the sentence in 9.2  class.mem paragraph 4 to read:

A member-declarator can contain a constant-initializer only if it declares a static member (9.4  class.static ) of const integral or const enumeration type, see 9.4.2  class.static.data .



80. Class members with same name as class

Section: 9.2  class.mem     Status: DR     Submitter: Jason Merrill     Date: 5 Dec 1998     Drafting: Merrill

From reflector message core-7917.

Between the May '96 and September '96 working papers, the text in 9.2  class.mem paragraph 13:

If T is the name of a class, then each of the following shall have a name different from T:
was changed by removing the word 'static'. Looking over the meeting minutes from Stockholm, none of the proposals seem to include this change, which breaks C compatibility and is not mentioned in the compatibility annex. Was this change actually voted in by the committee?

Specifically, this breaks /usr/include/netinet/in.h under Linux, in which "struct ip_opts" shares its name with one of its members.

Proposed resolution (10/00):

  1. Change the first bullet of 9.2  class.mem paragraph 13 to say
  2. Add another paragraph before 9.2  class.mem paragraph 14, reading
    In addition, if class T has a user-declared constructor (12.1  class.ctor ), every nonstatic data member of class T shall have a name different from T.



190. Layout-compatible POD-struct types

Section: 9.2  class.mem     Status: DR     Submitter: Steve Adamczyk     Date: 20 Dec 1999     Priority: 3     Drafting: Merrill

From reflector messages 8411-12.

The definition of layout-compatible POD-struct types in 9.2  class.mem paragraph 14 requires that the two types

have the same number of members, and corresponding members (in order) have layout-compatible types (3.9).
There does not appear to be any reason for including member functions and static data members in this requirement. It would be more logical to require only that the non-static data members of the two types must match.

The characteristics of layout-compatible types are not well described in the current wording, either. Apart from their use in 9.2  class.mem paragraph 16 to define the term "common initial sequence," there appears to be nothing said about which operations are possible between objects of layout-compatible types. For example, 3.9  basic.types paragraphs 2-3 give certain guarantees regarding use of memcpy on objects of the same type; it might be reasonable to assume that the same kinds of guarantees might apply to objects of layout-compatible types, but that is not said. Similarly, 3.10  basic.lval paragraph 15 describes permissible "type punning" but does not mention layout-compatible types.

Proposed resolution (10/00):

In 9.2  class.mem paragraphs 14 and 15, change all occurrences of "members" to "nonstatic data members."




67. Evaluation of left side of object-expression

Section: 9.4  class.static     Status: DR     Submitter: Mike Miller     Date: 6 Oct 1998

Paragraph 2 says that "the object-expression is always evaluated" when the class member syntax is used to refer to a static member. This presumably should say that the object expression is evaluated if the member access is performed, i.e., not if the overall expression is the operand of sizeof or the unevaluated branch of ?:, ||, or &&.

Proposed Resolution (10/99): Replace "is always evaluated" by "is evaluated" in 9.4  class.static paragraph 2.




48. Definitions of unused static members

Section: 9.4.2  class.static.data     Status: DR     Submitter: Bill Gibbons     Date: 23 Nov 1997

Also see section: 3.2  basic.def.odr .

Originally, all static data members still had to be defined outside the class whether they were used or not.

But that restriction was supposed to be lifted so that static data members need not be defined outside the class unless they are used in a manner which requires their definition, in the same manner as namespace-scope variables. In particular, if an integral/enum const static data member is initialized within the class, and its address is never taken, we agreed that no namespace-scope definition was required.

For example:

    struct A {
        static const int size = 10;
        int array[size];
    };
    
    int main() {
        A a;
        return 0;
    }
However, 9.4.2  class.static.data paragraph 4 says:
The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.
A narrow interpreration of "used" in this rule would make the example ill-formed because there is no namespace-scope definition of "size". A better wording for this rule would be:
The member shall still be defined in a namespace scope if it is used in the program in the manner described in 3.2  basic.def.odr . The namespace scope definition shall not contain an initializer.
Also, the wording in 3.2  basic.def.odr paragraph 2:
An expression is potentially evaluated unless either it is the operand of the sizeof operator (5.3.3  expr.sizeof ), or it is the operand of the typeid operator and does not designate an lvalue of polymorphic class type (5.2.8  expr.typeid ).
is incomplete because it does not mention the use of a compile-time constant as an array bound or template argument. It should say something like:
An expression is potentially evaluated unless it is the operand of the sizeof operator (5.3.3  expr.sizeof ), the operand of the typeid operator, an integral constant-expression used as an array bound or an integral constant-expression used as a template-argument for a non-reference template-parameter; and the expression does not designate an lvalue of polymorphic class type (5.2.8  expr.typeid ).

Proposed Resolution (04/99): Change the first sentence of 3.2  basic.def.odr paragraph 2 from:

An expression is potentially evaluated unless either it is the operand of the sizeof operator (5.3.3  expr.sizeof ), or it is the operand of the typeid operator and does not designate an lvalue of polymorphic class type (5.2.8  expr.typeid ).
to:
An expression is potentially evaluated unless it appears where an integral constant expression is required (see 5.19  expr.const ), is the operand of the sizeof operator (5.3.3  expr.sizeof ), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (5.2.8  expr.typeid ).



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

Section: 11  class.access     Status: DR     Submitter: Mike Ball     Date: unknown     Priority: 3

(Previously numbered 898.)

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.




9. Clarification of access to base class members

Section: 11.2  class.access.base     Status: DR     Submitter: unknown     Date: unknown     Drafting: Wilcox

(Previously numbered 899.)

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: DR     Submitter: unknown     Date: unknown     Drafting: Wilcox

(Previously numbered 924.)

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




142. Injection-related errors in access example

Section: 11.2  class.access.base     Status: DR     Submitter: Steve Adamczyk     Date: 16 Jul 1999     Drafting: Adamczyk

From reflector messages 8217-19.

In the example in paragraph 3 of 11.2  class.access.base , all the references to B in DD::f() should be replaced by ::B. The reason is that the class name B is private in D and thus inaccessible in DD. (The example was probably not updated when class name injection was added.)

Proposed resolution (10/00):

Replace the example in 11.2  class.access.base paragraph 3 with:

    class B {
    public:
        int mi;                 // nonstatic member
        static int si;          // static member
    };
    class D: private B {
    };
    class DD: public D {
        void f();
    };
    void DD::f() {
        mi = 3;                 // error: mi is private in D
        si = 3;                 // error: si is private in D
        ::B b;
        b.mi = 3;               // OK (b.mi is different from this->mi)
        b.si = 3;               // OK (b.si is different from this->si)
        ::B::si = 3;            // OK
        ::B* bp1 = this;        // error: B is a private base class
        ::B* bp2 = (::B*)this;  // OK with cast
        bp2->mi = 3;            // OK: access through a pointer to B
    }



161. Access to protected nested type

Section: 11.5  class.protected     Status: DR     Submitter: Steve Adamczyk     Date: 26 Aug 1999

11.5  class.protected paragraph 1 begins:

When a friend or a member function of a derived class references a protected nonstatic member of a base class, an access check applies in addition to those described earlier in clause 11  class.access .

This was intended to refer to nonstatic member functions and nonstatic data members. However, a protected nested type declared in a base class is, by some definition of the word, a "nonstatic" member, and therefore subject to this additional access check.

Proposed resolution (10/99): change "protected nonstatic member" in the above to "protected nonstatic member function or protected nonstatic data member" to make the intent clear.




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: DR     Submitter: Josee Lajoie     Date: unknown     Priority: 2

(Previously numbered 900.)

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: DR     Submitter: Daveed Vandevoorde     Date: 29 Sep 1998     Priority: 2     Drafting: O'Riordan

From reflector message core-7855.

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.




194. Identifying constructors

Section: 12.1  class.ctor     Status: DR     Submitter: Jamie Schmeiser     Date: 11 Jan 2000     Drafting: Miller

From reflector messages 8433-38.

According to 12.1  class.ctor paragraph 1, the syntax used in declaring a constructor allows at most one function-specifier. It is thus not permitted to declare a constructor both inline and explicit. This seems overly restrictive.

On a related note, there doesn't seem to be any explicit prohibition against member functions with the same name as the class. (Such a prohibition might reasonably be expected to occur in 9.2  class.mem paragraph 13, but member functions are not listed there.)

One possible interpretation would be that such member functions would violate the restrictions in 3.3.6  basic.scope.class paragraph 1, because the class name would refer to the class at some points in the class scope and to the member function at others. However, this seems a bit tenuous. Is an explicit prohibition needed?

(See also issue 147.)

Proposed resolution (10/00):

  1. Add to 9.2  class.mem paragraph 13

    • every member function of class T [Note: this restriction does not apply to constructors, which do not have names (12.1  class.ctor). ];

    immediately following the line

    • every data member of class T;
  2. Change 12.1  class.ctor paragraph 1 from

    A special declarator syntax using an optional function-specifier (7.1.2  dcl.fct.spec)...

    to

    A special declarator syntax using an optional sequence of function-specifiers (7.1.2  dcl.fct.spec)...



124. Lifetime of temporaries in default initialization of class arrays

Section: 12.2  class.temporary     Status: DR     Submitter: Jack Rouse     Date: 3 June 1999     Drafting: Adamczyk

From reflector message 8093.

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.




201. Order of destruction of temporaries in initializers

Section: 12.2  class.temporary     Status: DR     Submitter: Alan Nash     Date: 31 Jan 2000     Drafting: Adamczyk

From reflector messages 8516, 8519-20.

According to 12.2  class.temporary paragraph 4, an expression appearing as the initializer in an object definition constitutes a context "in which temporaries are destroyed at a different point than the end of the full-expression." It goes on to say that the temporary containing the value of the expression persists until after the initialization is complete (see also issue 117). This seems to presume that the end of the full-expression is a point earlier than the completion of the initialization.

However, according to 1.9  intro.execution paragraphs 12-13, the full-expression in such cases is, in fact, the entire initialization. If this is the case, the behavior described for temporaries in an initializer expression is simply the normal behavior of temporaries in any expression, and treating it as an exception to the general rule is both incorrect and confusing.

Proposed resolution (04/01):

[Note: this proposal also addresses issue 124.]

  1. Add to the end of 1.9  intro.execution paragraph 12:

    If the initializer for an object or sub-object is a full-expression, the initialization of the object or sub-object (e.g., by calling a constructor or copying an expression value) is considered to be part of the full-expression.
  2. Replace 12.2  class.temporary paragraph 4 with:

    There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default constructor is called to initialize an element of an array. If the constructor has one or more default arguments, any temporaries created in the default argument expressions are destroyed immediately after return from the constructor.



152. explicit copy constructors

Section: 12.3.1  class.conv.ctor     Status: DR     Submitter: Steve Adamczyk     Date: 4 August 1999     Drafting: Adamczyk

From reflector messages 8237-8.

Can a copy-constructor declared as explicit be used to copy class values implicitly? For example,

   struct X {
      X();
      explicit X(const X&);
   };
   void f(X);
   int main() { X x; f(x); }
According to 12.3.1  class.conv.ctor paragraphs 2-3,
An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5  dcl.init ) or where casts (5.2.9  expr.static.cast , 5.4  expr.cast ) are explicitly used... A copy-constructor (12.8  class.copy ) is a converting constructor. An implicitly-declared copy constructor is not an explicit constructor; it may be called for implicit type conversions.
This passage would appear to indicate that the call in the example is ill-formed, since it uses neither the direct-initialization syntax nor an explicit cast. The last sentences are especially interesting in this regard, indicating that explicit and non-explicit copy constructors are handled differently.

On the other hand, 8.5  dcl.init paragraph 14, bullet 4, sub-bullet 2 says,

If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination... [the] applicable constructors are enumerated (13.3.1.3  over.match.ctor )...
The cited passage says that
The candidate functions are all the constructors of the class of the object being initialized.

Notes from 04/01 meeting:

After the issue was accepted as a DR with the proposed resolution to change 13.3.1.3  over.match.ctor paragraph 1 as described below, it was noticed that 12.3.1  class.conv.ctor paragraph 3 states that:

A copy-constructor (12.8  class.copy) is a converting constructor.

In addition to making the proposed resolution for this issue ineffectual, the wording of paragraph 3 also contradicts that of paragraph 1:

A constructor declared without the function-specifier explicit that can be called with a single parameter specifies a conversion from the type of its first parameter to the type of its class. Such a constructor is called a converting constructor.

These considerations led to the addition of the second point of the proposed resolution.

Proposed resolution (04/01):

  1. Change the first two sentences of 13.3.1.3  over.match.ctor paragraph 1 to

    When objects of class type are direct-initialized (8.5  dcl.init), or copy-initialized from an expression of the same or a derived class type (8.5  dcl.init), overload resolution selects the constructor. For direct-initialization, the candidate functions are all the constructors of the class of the object being initialized. For copy-initialization, the candidate functions are all the converting constructors (12.3.1  class.conv.ctor ) of that class.
  2. Change the first sentence of 12.3.1  class.conv.ctor paragraph 3 to read:

    A non-explicit copy constructor (12.8  class.copy) is a converting constructor.



193. Order of destruction of local automatics of destructor

Section: 12.4  class.dtor     Status: DR     Submitter: Gerhard Menzl     Date: 7 Jan 2000     Drafting: Crowl

The Standard is not clear whether automatic objects in a destructor are destroyed before or after the destruction of the class's base and member subobjects. That is, given

    struct S { ~S(); };

    struct T {
        S x;
        ~T() {
            S y;
        };
    };

which will be destroyed first, x or y?

Proposed resolution (10/00):

In 12.4  class.dtor paragraph 6, change

A destructor for class X calls the destructors for X's direct members, ...
to
After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X's direct members, ...




235. Assignment vs initialization

Section: 12.6.2  class.base.init     Status: DR     Submitter: Mike Miller     Date: 16 Sep 2000

In 12.6.2  class.base.init paragraph 4 we read:

After the call to a constructor for class X has completed, if a member of X is neither specified in the constructor's mem-initializers, nor default-initialized, nor initialized during execution of the body of the constructor, the member has indeterminate value.

Using the term "initialized" to describe setting the value of a member inside the body of a constructor is a misuse of the term: only by use of a placement new expression can a member be initialized "during the execution of the body of the constructor."

Suggested resolution: Change "initialized" to "given a value."

Proposed resolution (10/00): As suggested.




20. Some clarifications needed for 12.8 para 15

Section: 12.8  class.copy     Status: DR     Submitter: unknown     Date: unknown

(Previously numbered 931.)

Issue 1

12.8  class.copy (From J16/99-0005 = WG21 N1182, "Proposed Resolutions for Core Language Issues 6, 14, 20, 40, and 89")

There are three related sub-issues in this issue, all dealing with the elision of copy constructors as described in 12.8  class.copy paragraph 15:

  1. The text should make clear that the requirement that the copy constructor be accessible and unambiguous is not relaxed in cases where a call to a copy constructor is elided.
  2. It is not clear from the text that the two optimizations described can be applied transitively, and, if so, the implications for the order of destruction are not spelled out.
  3. The text should exclude applying the function-return optimization if the expression names a static or volatile local object.
Analysis

After discussion in Santa Cruz, the core group decided that sub-issue #1 required no change; the necessity of an accessible and unambiguous copy constructor is made clear in 12.2  class.temporary paragraph 1 and need not be repeated in this text. The remaining two sub-issues appear to be valid criticisms and should be addressed.

Proposed Resolution (10/99):

[Note: a small portion of this wording is superseded by the resolution of issue 185.]

The paragraph in question should be rewritten as follows. In addition, references to this section should be added to the index under "temporary, elimination of," "elimination of temporary," and "copy, constructor elision."

Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing: the copying of the local automatic object t into the temporary object for the return value of function f() and the copying of that temporary object into object t2. Effectively, the construction of the local object t can be viewed as directly initializing the global object t2, and that object's destruction will occur at program exit. —end example]


185. "Named" temporaries and copy elision

Section: 12.8  class.copy     Status: DR     Submitter: Bill Wade     Date: 11 Nov 1999     Drafting: Miller

12.8  class.copy paragraph 15 refers only to "temporary class objects." It needs to be made clear that these provisions do not apply to temporaries that have been bound to references. For instance,

    struct A {
        mutable int value;
        explicit A(int i) : value(i) {}
        void mutate(int i) const { value = i; }
    };

    int foo() {
        A const& t = A(1);
        A n(t);          // can this copy be elided?
        t.mutate(2);
        return n.value;  // can this return 2?
    }
The current wording seems to allow an implementation not to perform the copy in A N(t) because the source object is a temporary (created explicitly by A(1)).

Proposed resolution (10/00):

Change the wording proposed in the resolution of issue 20 from

to




239. Footnote 116 and Koenig lookup

Section: 13.3.1.1.1  over.call.func     Status: DR     Submitter: Steve Clamage     Date: 2 Aug 2000     Priority: 1     Drafting: Adamczyk

From messages 8848 and 8850.

In describing non-member functions in an overload set, footnote 116 (13.3.1.1.1  over.call.func) says,
Because of the usual name hiding rules, these will be introduced by declarations or by using-directives all found in the same block or all found at namespace scope.

At least in terms of the current state of the Standard, this is not correct: a block extern declaration does not prevent Koenig lookup from occurring. For example,

    enum E { zero };
    void f(E);
    void g() {
        void f(int);
        f(zero);
    }

In this example, the overload set will include declarations from both namespace and block scope.

(See also issue 12.)

Proposed resolution (04/01):

  1. In 3.4.2  basic.lookup.koenig paragraph 2, change

    If the ordinary unqualified lookup of the name finds the declaration of a class member function, the associated namespaces and classes are not considered.

    to

    If the ordinary unqualified lookup of the name finds the declaration of a class member function, or a block-scope function declaration that is not a using-declaration, the associated namespaces and classes are not considered.

    and change the example to:

        namespace NS {
            class T { };
            void f(T);
            void g(T, int);
        }
        NS::T parm;
        void g(NS::T, float);
        int main() {
            f(parm);            // OK: calls NS::f
            extern void g(NS::T, float);
            g(parm, 1);         // OK: calls g(NS::T, float)
        }
    
  2. In 13.3.1.1.1  over.call.func paragraph 3 from:

    If the name resolves to a non-member function declaration, that function and its overloaded declarations constitute the set of candidate functions.

    to

    If the name resolves to a set of non-member function declarations, that set of functions constitutes the set of candidate functions.

    Also, remove the associated footnote 116.




59. Clarification of overloading and UDC to reference type

Section: 13.3.1.4  over.match.copy     Status: DR     Submitter: Steve Adamczyk     Date: 13 Oct 1998

Sections 13.3.1.4  over.match.copy and 13.3.1.5  over.match.conv should be clarified regarding the treatment of conversion functions which return reference types.

Proposed resolution (10/99):

In 13.3.1.4  over.match.copy paragraph 1, change

Conversion functions that return "reference to T" return lvalues of type T and are therefore considered to yield T for this process of selecting candidate functions.
to
Conversion functions that return "reference to X" return lvalues of type X and are therefore considered to yield X for this process of selecting candidate functions.
In 13.3.1.5  over.match.conv paragraph 1, change
Conversion functions that return "reference to T" return lvalues of type T and are therefore considered to yield T for this process of selecting candidate functions.
to
Conversion functions that return "reference to cv2 X" return lvalues of type "cv2 X" and are therefore considered to yield X for this process of selecting candidate functions.



51. Overloading and user-defined conversions

Section: 13.3.3  over.match.best     Status: DR     Submitter: Steve Adamczyk     Date: 13 Oct 1998

In 13.3.3  over.match.best paragraph 1, bullet 4 of the second set of bullets, there is a cross-reference to 8.5  dcl.init and 13.3.1.5  over.match.conv . I believe it should also reference 13.3.1.6  over.match.ref . I think the phrase "initialization by user-defined conversion" was intended to refer to all initializations using user-defined conversions, and not just the case in 13.3.1.5  over.match.conv . Referring to only 13.3.1.5  over.match.conv suggests a narrower meaning of the phrase.

13.3.1.4  over.match.copy , although it does deal with initialization by user-defined conversion, does not need to be referenced because it deals with class —> class cases, and therefore there are no standard conversions involved that could be compared.




84. Overloading and conversion loophole used by auto_ptr

Section: 13.3.3.1  over.best.ics     Status: DR     Submitter: Steve Adamczyk     Date: 10 Dec 1998     Priority: 1

By the letter of the standard, the conversions required to make auto_ptr work should be accepted.

However, there's good reason to wonder if there isn't a bug in the standard here. Here's the issue: line 16 in the example below comes down to

copy-initialize an auto_ptr<Base> from an auto_ptr<Derived> rvalue
To do that, we first look to see whether we can convert an auto_ptr<Derived> to an auto_ptr<Base>, by enumerating the constructors of auto_ptr<Base> and the conversion functions of auto_ptr<Derived>. There's a single possible way to do the conversion, namely the conversion function

    auto_ptr<Derived>::operator auto_ptr<Base>()
(generated from the template). (The constructor auto_ptr<Base>(auto_ptr_ref<Base>) doesn't work because it requires a user-defined conversion on the argument.)

So far, so good. Now, we do the copy step:

direct-initialize an auto_ptr<Base> from an auto_ptr<Base> rvalue
This, as we've gone to great lengths to set up, is done by calling the conversion function
    auto_ptr<Base>::operator auto_ptr_ref<Base>()
(generated from the template), and then the constructor
    auto_ptr<Base>(auto_ptr_ref<Base>)
(generated from the template).

The problem with this interpretation is that it violates the long-standing common-law rule that only a single user-defined conversion will be called to do an implicit conversion. I find that pretty disturbing. (In fact, the full operation involves two conversion functions and two constructors, but "copy" constructors are generally considered not to be conversions.)

The direct-initialization second step of a copy-initialization was intended to be a simple copy — you've made a temporary, and now you use a copy constructor to copy it. Because it is defined in terms of direct initialization, however, it can exploit the loophole that auto_ptr is based on.

To switch to personal opinion for a second, I think it's bad enough that auto_ptr has to exploit a really arcane loophole of overload resolution, but in this case it seems like it's exploiting a loophole on a loophole.

    struct Base {                             //  2
       static void sink(auto_ptr<Base>);      //  3
    };                                        //  4

    struct Derived : Base {                   //  5
       static void sink(auto_ptr<Derived>);   //  6
    };                                        //  7

    auto_ptr<Derived> source() {              //  8
       auto_ptr<Derived> p(source());         //  9
       auto_ptr<Derived> pp(p);               // 10
       Derived::sink(source());               // 11
       p = pp;                                // 12
       p = source();                          // 13
       auto_ptr<Base> q(source());            // 14
       auto_ptr<Base> qp(p);                  // 15
       Base::sink(source());                  // 16
       q = pp;                                // 17
       q = source();                          // 18
       return p;                              // 19
       return source();
    }
Derek Inglis: (in core-8564)

It seems clear to me that the result of this direct initilization must be the second standard conversion sequence in a user defined conversion sequence. Otherwise the resulting conversion sequence is not an implicit conversion sequence. By the letter of the standard, the sequence of conversions making up a copy-initialization must be an implicit conversion sequence.

Paragraph 3 of clause 4  conv:

An expression e can be implicitly converted to a type T if and only if the declaration "T t=e;" is well-formed, for some invented temporary variable t (8.5  dcl.init).

Paragraph 1 of 13.3.3.1  over.best.ics:

An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called. The sequence of conversions is an implicit conversion as defined in clause 4  conv, which means it is governed by the rules for initialization of an object or reference by a single expression (8.5  dcl.init, 8.5.3  dcl.init.ref).
Sentence 1 of paragraph 12 of 8.5  dcl.init:
The initialization that occurs in argument passing ... is called copy-initialization and is equivalent to the form
     T x = a;

For me, these sentences imply that all sequences of conversions permitted on a function argument must be valid implicit conversion sequences.

The 'loophole' can be closed by adding a sentence (or note) to the section describing the 'direct initialization second step of a copy initialization' stating that the copy initialization is ill-formed if the conversion sequence resulting from the direct initialization is not a standard conversion sequence.

(See also issue 177 and paper J16/00-0009 = WG21 N1232.)

Proposed resolution (10/00):

Change 13.3.3.1  over.best.ics paragraphs 3 and 4 from

Except in the context of an initialization by user-defined conversion (13.3.1.4  over.match.copy, 13.3.1.5  over.match.conv), a well-formed implicit conversion sequence is one of the following forms:

In the context of an initialization by user-defined conversion (i.e., when considering the argument of a user-defined conversion function; see 13.3.1.4  over.match.copy, 13.3.1.5  over.match.conv), only standard conversion sequences and ellipsis conversion sequences are allowed.

to

A well-formed implicit conversion sequence is one of the following forms:

However, when considering the argument of a user-defined conversion function that is a candidate by 13.3.1.3  over.match.ctor when invoked for the copying of the temporary in the second step of a class copy-initialization, or by 13.3.1.4  over.match.copy, 13.3.1.5  over.match.conv, or 13.3.1.6  over.match.ref in all cases, only standard conversion sequences and ellipsis conversion sequences are allowed.




83. Overloading and deprecated conversion of string literal

Section: 13.3.3.2  over.ics.rank     Status: DR     Submitter: Steve Adamczyk     Date: 24 Jan 1999     Drafting: Adamczyk

In 13.3.3.2  over.ics.rank , we have

This does not work right with respect to the deprecated conversion from string literal to "char *". Consider
    void f(char *);
    void f(const char *);
    
    f("abc");
The two conversion sequences differ only in their qualification conversions, and the destination types are similar. The cv-qualification signature of "char *", is a proper subset of the cv-qualification signature of "const char *", so f(char *) is chosen, which is wrong. The rule should be like the one for conversion to bool — the deprecated conversion should be worse than another exact match that is not the deprecated conversion.

Proposed resolution (10/00):

Change 13.3.3.2  over.ics.rank paragraph 3 bullet 1 sub-bullet 3 from

S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (4.4  conv.qual ), respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification signature of type T2.
to
S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (4.4  conv.qual ), respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification signature of type T2, and S1 is not the deprecated string literal array-to-pointer conversion (4.2  conv.array ).



153. Misleading wording (rank of conversion)

Section: 13.3.3.2  over.ics.rank     Status: DR     Submitter: Valentin Bonnard     Date: 6 Aug 1999     Drafting: Adamczyk

13.3.3.2  over.ics.rank paragraph 3 bullet 1 sub-bullet 2 says,

the rank of S1 is better than the rank of S2 (by the rules defined below)...
This wording is confusing. The word "below" refers to paragraph 4 (which may not be clear), and the bulk of paragraph 4 deals with comparing conversion sequences whose "rank" is the same.

Proposed resolution (10/00):

In 13.3.3.2  over.ics.rank paragraph 3, change

the rank of S1 is better than the rank of S2 (by the rules defined below)
to
the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below



202. Use of overloaded function name

Section: 13.4  over.over     Status: DR     Submitter: Steve Clamage     Date: 2 Feb 2000     Drafting: Spicer

From reflector messages 8521 and 8524.

13.4  over.over paragraph 1 contains a supposedly exhaustive list of contexts in which the name of an overloaded function can be used without an argument list ("...shall not be used without arguments in contexts other than those listed"). However, 14.3.2  temp.arg.nontype paragraph 5, bullet 4 gives another context: as a template nontype argument.

Suggested resolution: Add the missing case to 13.4  over.over.

Proposed resolution (10/00):

Add as the final bullet in 13.4  over.over paragraph 1:

and adjust the "or" and final period on the preceding two bullets.




250. Address of function template specialization with non-deduced template arguments

Section: 13.4  over.over     Status: DR     Submitter: Nikolas Kauer     Date: 10 Oct 2000

13.4  over.over paragraph 2 says,

If the name is a function template, template argument deduction is done (14.8.2.2  temp.deduct.funcaddr), and if the argument deduction succeeds, the deduced template arguments are used to generate a single template function, which is added to the set of overloaded functions considered.

It is not clear whether this formulation allows explicit specification of non-deduced template arguments. For instance,

    template <int I> void f(double x[]);
    typedef void (*FPtr)(double x[]);
    FPtr fp = &f<3>;

If only deduced arguments can be used, this example is ill-formed.

Suggested resolution: Clarify 13.4  over.over paragraph 2 to allow both deduced and explicitly-specified template arguments to be used to determine the function template specialization to be added to the overload set.

(See also issues 115 and 214.)

Proposed resolution (10/00):

In 13.4  over.over paragraph 2, change

...if the argument deduction succeeds, the deduced template arguments are used to generate a single template function...

to

...if the argument deduction succeeds, the resulting template argument list is used to generate a single function template specialization...



221. Must compound assignment operators be member functions?

Section: 13.5.3  over.ass     Status: DR     Submitter: Jim Hyslop     Date: 3 Apr 2000     Drafting: Merrill

Is the intent of 13.5.3  over.ass paragraph 1 that all assignment operators be non-static member functions (including operator+=, operator*=, etc.) or only simple assignment operators (operator=)?

Notes from 04/00 meeting:

Nearly all references to "assignment operator" in the IS mean operator= and not the compound assignment operators. The ARM was specific that this restriction applied only to operator=. If it did apply to compound assignment operators, it would be impossible to overload these operators for bool operands.

Proposed resolution (04/01):

  1. Change the title of 5.17  expr.ass from "Assignment operators" to "Assignment and compound assignment operators."

  2. Change the first sentence of 5.17  expr.ass paragraph 1 from

    There are several assignment operators, all of which group right-to-left. All require a modifiable lvalue as their left operand, and the type of an assignment expression is that of its left operand. The result of the assignment operation is the value stored in the left operand after the assignment has taken place; the result is an lvalue.

    to

    The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue with the type and value of the left operand after the assignment has taken place.

Additional note (10/00): Paragraphs 2-6 of 5.17  expr.ass should all be understood to apply to simple assignment only and not to compound assignment operators.




32. Clarification of explicit instantiation of non-exported templates

Section: 14  temp     Status: DR     Submitter: Daveed Vandevoorde     Date: 10 Jul 1998

From reflector message core-7766.

Section 14  temp paragraph 8 says:

A non-exported template that is neither explicitly specialized nor explicitly instantiated must be defined in every translation unit in which it is implicitly instantiated (14.7.1  temp.inst ) or explicitly instantiated (14.7.2  temp.explicit ); no diagnostic is required.
Shouldn't the first underlined phrase be omitted to avoid conflict with the second underlined phrase?

From John Spicer:

The first "explicitly instantiated" is intended to mean "explicitly instantiated in some other translation unit".

Proposed Resolution (04/99): Change the text in 14  temp paragraph 8 from:

A non-exported template that is neither explicitly specialized nor explicitly instantiated must be defined in every translation unit in which it is implicitly instantiated (14.7.1  temp.inst ) or explicitly instantiated (14.7.2  temp.explicit ); no diagnostic is required.
to:
A non-exported template must be defined in every translation unit in which it is implicitly instantiated (14.7.1  temp.inst ), unless the corresponding specialization is explicitly instantiated (14.7.2  temp.explicit ) in some translation unit; no diagnostic is required. [Note: See also 14.7.2  temp.explicit ]



105. Meaning of "template function"

Section: 14  temp     Status: DR     Submitter: Daveed Vandevoorde     Date: 16 Apr 1999     Drafting: Vandevoorde

The phrase "template function" is sometimes used to refer to a template (e.g., in 14  temp paragraph 8) and sometimes to refer to a function generated from a template (e.g., 13.4  over.over paragraph 4).

Suggested Resolution:

The phrase should mean "a function generated from a template" (or might perhaps include explicit specializations).

Proposed resolution (10/00):

In 1.3.10  defns.signature paragraph 10, replace "template function specialization" by "function template specialization".

In 9.3  class.mfct paragraph 2, replace "template member functions" by "member functions of class templates and member function templates."

In 13.3.1  over.match.funcs paragraph 7 and footnote, replace all instances of "template functions" by "function template specializations."

In 13.3.3  over.match.best paragraph 1, fourth bullet (counting all bullets in that paragraph), replace "template function specialization" by "function template specialization". In the fifth bullet, replace "template functions" by "function template specializations."

In 13.4  over.over paragraph 2, replace "template function" by "function template specialization."

Change 13.4  over.over paragraph 4 from:

If more than one function is selected, any template functions in the set are eliminated if the set also contains a non-template function, and any given template function is eliminated if the set contains a second template function that is more specialized than the first according to the partial ordering rules of 14.5.5.2  temp.func.order. After such eliminations, if any, there shall remain exactly one selected function.
to:
If more than one function is selected, any function template specializations in the set are eliminated if the set also contains a non-template function, and any given function template specialization F1 is eliminated if the set contains a second function template specialization whose function template is more specialized than the function template of F1 according to the partial ordering rules of 14.5.5.2  temp.func.order. After such eliminations, if any, there shall remain exactly one selected function.

Change text in section 14  temp paragraph 8 from:

A template function declared both exported and inline is just inline and not exported.
to:
A function template declared both exported and inline is just inline and not exported.

In 14.5.3  temp.friend paragraph 1, third bullet, replace "template function" by "function template" and "function specialization" by "function template specialization."

In footnote 130 (14.5.5  temp.fct paragraph 2), replace "template functions" by "function template specializations."

In 14.5.5.2  temp.func.order paragraph 1, third bullet change "template function specialization" to "function template specialization".

In 14.8.2  temp.deduct paragraph 1, change "template function specialization" to "function template specialization".

In 17.1.5  defns.component change "non-member template functions that operate" to "non-member function templates that operate".

In 17.1.18  defns.traits change "template classes and template functions" to "class templates and function templates".

In 20.2  lib.utility paragraph 1 change:

This subclause contains some basic template functions and classes that are used throughout the rest of the library.
to:
This subclause contains some basic function and class templates that are used throughout the rest of the library.

In 20.2.2  lib.pairs paragrah 1 change "template function" to "function template".

In footnote 215 (20.3.7  lib.function.pointer.adaptors paragraph 6) change "template functions" to "function templates".

In 22.1.1  lib.locale paragraph 4 change "template function" to "function template".

In 24.1  lib.iterator.requirements paragraph 2 change "template function" to "function template".

In 24.3.3  lib.std.iterator.tags paragraph 1, change "template function" to "function template specialization."

In 24.3.4  lib.iterator.operations paragraph 1 change "template function" to "function template", and "These functions use" to "These function templates use".

In the section heading of 27.6.2.5.4  lib.ostream.inserters.character change "template functions" to "function templates".

In 17.3.1.2  lib.structure.requirements paragraph 2 change "template class name char_traits" to "class template char_traits".

In the section heading of 18.2.1.1  lib.numeric.limits change "Template class" to "Class template".

In 20.1.5  lib.allocator.requirements paragraph 3 change "template class member rebind" to "member class template rebind" and change "template typedef" to "typedef template".

In the section heading of 20.3.6.1  lib.binder.1st change "Template class" to "Class template".

In the section heading of 20.3.6.3  lib.binder.2nd change "Template class" to "Class template".

In the section heading of 20.4.5  lib.auto.ptr change "Template class" to "Class template".

In the section heading of 21.3  lib.basic.string change "Template class" to "Class template".

In 21.3  lib.basic.string paragraphs 1 and 2 change "template class basic_string" to "class template basic_string".

In the section heading of 22.2.1.1  lib.locale.ctype change "Template class" to "Class template".

In the section heading of 22.2.1.2  lib.locale.ctype.byname change "Template class" to "Class template".

In the section heading of 22.2.1.5  lib.locale.codecvt change "Template class" to "Class template".

In the section heading of 22.2.1.6  lib.locale.codecvt.byname change "Template class" to "Class template".

In the section heading of 22.2.2.1  lib.locale.num.get change "Template class" to "Class template".

In the section heading of 22.2.2.2  lib.locale.nm.put change "Template class" to "Class template".

In the section heading of 22.2.3.1  lib.locale.numpunct change "Template class" to "Class template".

In the section heading of 22.2.3.2  lib.locale.numpunct.byname change "Template class" to "Class template".

In the section heading of 22.2.4.1  lib.locale.collate change "Template class" to "Class template".

In the section heading of 22.2.4.2  lib.locale.collate.byname change "Template class" to "Class template".

In the section heading of 22.2.5.1  lib.locale.time.get change "Template class" to "Class template".

In the section heading of 22.2.5.2  lib.locale.time.get.byname change "Template class" to "Class template".

In the section heading of 22.2.5.3  lib.locale.time.put change "Template class" to "Class template".

In the section heading of 22.2.5.4  lib.locale.time.put.byname change "Template class" to "Class template".

In the section heading of 22.2.6.1  lib.locale.money.get change "Template class" to "Class template".

In the section heading of 22.2.6.2  lib.locale.money.put change "Template class" to "Class template".

In the section heading of 22.2.6.3  lib.locale.moneypunct change "Template class" to "Class template".

In the section heading of 22.2.6.4  lib.locale.moneypunct.byname change "Template class" to "Class template".

In the section heading of 22.2.7.1  lib.locale.messages change "Template class" to "Class template".

In the section heading of 22.2.7.2  lib.locale.messages.byname change "Template class" to "Class template".

In the section heading of 23.2.1  lib.deque change "Template class" to "Class template".

In the section heading of 23.2.2  lib.list change "Template class" to "Class template".

In the section heading of 23.2.3.1  lib.queue change "Template class" to "Class template".

In the section heading of 23.2.3.2  lib.priority.queue change "Template class" to "Class template".

In the section heading of 23.2.3.3  lib.stack change "Template class" to "Class template".

In the section heading of 23.2.4  lib.vector change "Template class" to "Class template".

In the section heading of 23.3.1  lib.map change "Template class" to "Class template".

In the section heading of 23.3.2  lib.multimap change "Template class" to "Class template".

In the section heading of 23.3.3  lib.set change "Template class" to "Class template".

In the section heading of 23.3.4  lib.multiset change "Template class" to "Class template".

In the section heading of 23.3.5  lib.template.bitset change "Template class" to "Class template".

In 23.3.5  lib.template.bitset paragraph 1, change "template class" to "class template".

In the section heading of 24.4.1.1  lib.reverse.iterator change "Template class" to "Class template".

In the section heading of 24.4.2.1  lib.back.insert.iterator change "Template class" to "Class template".

In the section heading of 24.4.2.3  lib.front.insert.iterator change "Template class" to "Class template".

In the section heading of 24.4.2.5  lib.insert.iterator change "Template class" to "Class template".

In 24.5  lib.stream.iterators paragraph 1, change "template classes" to "class templates".

In the section heading of 24.5.1  lib.istream.iterator change "Template class" to "Class template".

In the section heading of 24.5.2  lib.ostream.iterator [lib.ostream.iterator] change "Template class" to "Class template".

In the section heading of 24.5.3  lib.istreambuf.iterator change "Template class" to "Class template".

In 24.5.3  lib.istreambuf.iterator paragraph 1, change "template class" to "class template".

In the section heading of 24.5.3.1  lib.istreambuf.iterator::proxy change "Template class" to "Class template".

In the section heading of 24.5.4  lib.ostreambuf.iterator change "Template class" to "Class template".

In 24.5.4  lib.ostreambuf.iterator paragraph 1, change "template class" to "class template".

In 26.2  lib.complex.numbers paragraph 1, change "template class" to "class template".

In the section heading of 26.2.2  lib.complex change "Template class" to "Class template".

In 26.3.1  lib.valarray.synopsis paragraph 1, change "template classes" to "class templates" and change "function signatures" to "function templates".

In the section heading of 26.3.2  lib.template.valarray change "Template class" to "Class template".

In the section heading of 26.3.5  lib.template.slice.array change "Template class" to "Class template".

In the section heading of 26.3.7  lib.template.gslice.array change "Template class" to "Class template".

In the section heading of 26.3.8  lib.template.mask.array change "Template class" to "Class template".

In the section heading of 26.3.9  lib.template.indirect.array change "Template class" to "Class template".

In 27.2  lib.iostream.forward [lib.iostream.forward] paragraphs 3 to 7, change "template classes" to "class templates". [Note: Some editorial changes were made in paragraphs 2 to 8 when these changes were applied in September 2001.]

In the section heading of 27.4.3  lib.fpos change "Template class" to "Class template".

In the section heading of 27.4.4  lib.ios change "Template class" to "Class template".

In the section heading of 27.5.2  lib.streambuf change "Template class" to "Class template".

In 27.5.2  lib.streambuf paragraphs 2 and 3, change "template class" to "class template".

In the section heading of 27.6.1.1  lib.istream change "Template class" to "Class template".

In the section heading of 27.6.1.5  lib.iostreamclass change "Template class" to "Class template".

In the section heading of 27.6.2.1  lib.ostream change "Template class" to "Class template".

In 27.7  lib.string.streams paragraph 1 change "template classes" to "class templates".

In the section heading of 27.7.1  lib.stringbuf change "Template class" to "Class template".

In the section heading of 27.7.2  lib.istringstream change "Template class" to "Class template".

In the section heading of 27.7.4  lib.stringstream change "Template class" to "Class template".

In the section heading of 27.8.1.1  lib.filebuf change "Template class" to "Class template".

In the section heading of 27.8.1.5  lib.ifstream change "Template class" to "Class template".

In the section heading of 27.8.1.8  lib.ofstream change "Template class" to "Class template".

In the section heading of 27.8.1.11  lib.fstream change "Template class" to "Class template".




134. Template classes and declarator-ids

Section: 14  temp     Status: DR     Submitter: Mike Miller     Date: 17 June 1999     Drafting: Miller

From reflector message 8109.

14  temp paragraph 2 says,

[Note: in a class template declaration, if the declarator-id is a template-id, the declaration declares a class template partial specialization (14.5.4  temp.class.spec ). ]
There is no declarator-id in a class template declaration (cf paragraph 3).

Proposed resolution (10/00):

Replace the phrase "if the declarator-id is a template-id" with "if the class name is a template-id."




21. Can a default argument for a template parameter appear in a friend declaration?

Section: 14.1  temp.param     Status: DR     Submitter: unknown     Date: unknown     Drafting: Spicer

(Previously numbered 932.)

14.1  temp.param paragraph 10 says:

The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way as default function arguments are (8.3.6  dcl.fct.default )."
Can a default argument for a template argument appear in a friend declaration? If so, when is this default argument considered for template instantiations?

For example,

    template<class T1, class T2 = int> class A;
 
    class B {
        template<class T1 = int, class T2> friend class A;
    };
Is this well-formed? If it is, should the IS say when the default argument for T1 is considered for instantiations of class A?

Proposed resolution (10/00): Add to the end of 14.1  temp.param paragraph 9,

A default template-argument shall not be specified in a friend template declaration.

(See also issue 136.)




49. Restriction on non-type, non-value template arguments

Section: 14.1  temp.param     Status: DR     Submitter: Mike Miller     Date: 16 Oct 1998

The example in 14.1  temp.param paragraph 8 is:

    template<int* a> struct R { /*...*/ };
    int* p;
    R<p> w;
There was a French comment was that this is an error, and there was general agreement with that.

I've been looking for the verbiage that specifies that this is an error and haven't found it. In particular, nothing in 14.1  temp.param ("Template parameters") nor 14.3.2  temp.arg.nontype ("Template non-type arguments") appears to rule out this case. (14.3.2  temp.arg.nontype paragraph 1 allows an argument to be "the name of an object or function with external linkage," with no limitation on the kinds of parameters such a name can match; "p" is, in fact, such a name.)

Should the resolution of the French comment include beefing up one or both of these sections to cover the applicable rules explicitly?

Proposed Resolution (04/99): Change the example in 14.1  temp.param paragraph 8 from:

    template<int *a> struct R { /* ... */ };
    template<int b[5]> struct S { /* ... */ };
    int *p;
    R<p> w; // OK
    S<p> x; // OK due to parameter adjustment
    int v[5];
    R<v> y; // OK due to implicit argument conversion
    S<v> z; // OK due to both adjustment and conversion
to:
    template<int *a> struct R { /* ... */ };
    template<int b[5]> struct S { /* ... */ };
    int p;
    R<&p> w; // OK
    S<&p> x; // OK due to parameter adjustment
    int v[5];
    R<v> y; // OK due to implicit argument conversion
    S<v> z; // OK due to both adjustment and conversion
Furthermore, in 14.3.2  temp.arg.nontype paragraph 1:


187. Scope of template parameter names

Section: 14.1  temp.param     Status: DR     Submitter: John Spicer     Date: 15 Nov 1999

From reflector messages 8364-6, 8369, 8370, and 8372.

At the Dublin meeting (04/99), the Committee proposed to resolve issue 22 by simply changing the wording to make clear that a template parameter cannot be used in its own default argument. This creates a third treatment of this kind of situation, in addition to 3.3.1  basic.scope.pdecl paragraph 1, where declarators are in scope and can be used in their initializers, and paragraph 3, where an enumerator is not in scope until after its complete enumerator-definition. The Dublin resolution is for the template parameter to be in scope in its default argument but not usable. It would be more consistent to treat template parameters like enumerators: simply not in scope until the entire template-parameter declaration is seen.

On a related note, 14.1  temp.param paragraph 14 should be rewritten to connect the prohibition with visibility rules; otherwise, it sounds as if the following example is not permitted:

    const int Z = 1;
    template <int X = Z, int Z> class A {};

Notes from 04/00 meeting:

The core working group did not reach consensus on the suggested approach to issue 22. However, it was agreed that the intent expressed in the earlier resolution would be better served by different wording.

Proposed resolution (10/00):

[Note: This resolution supersedes the resolution to issue 22.]

Replace 14.1  temp.param paragraph 14 as follows:

A template parameter shall not be used in its own default argument.



30. Valid uses of "::template"

Section: 14.2  temp.names     Status: DR     Submitter: Daveed Vandevoorde     Date: 28 May 1998

From reflector message core-7731.

I have a request for clarification regarding a issue similar to John Wiegley's, but wrt. the ::template syntax. More precisely, where is

    X::template Y
allowed? (It is required for dependent X where Y is a template-id, I believe, but it doesn't seem to be disallowed elsewhere.)

The question also holds for '.template' and '->template'.

Proposed Resolution (04/99): Append to 14.2  temp.names paragraph 5:

Furthermore, names of member templates shall not be prefixed by the keyword template if the postfix-expression or qualified-id does not appear in the scope of a template. [Note: just as is the case with the typename prefix, the template prefix is allowed in cases where it is not strictly necessary; i.e., when the expression on the left of the -> or ., or the nested-name-specifier is not dependent on a template-parameter. ]



38. Explicit template arguments and operator functions

Section: 14.2  temp.names     Status: DR     Submitter: John Wiegley     Date: 17 Aug 1998     Drafting: Spicer/Schmeiser

From reflector message core-7807.

It appears from the grammar that explicit template arguments cannot be specified for overloaded operator names. Does this mean that template operators can never be friends?

But assuming that I read things wrong, then I should be able to specify a global template 'operator +' by writing:

    friend A::B operator + <>(char&);
John Spicer:

You should be able to have explicit template arguments on operator functions, but the grammar does seem to prohibit it (unless I'm reading it incorrectly). This is an error in the grammar, they should be permitted.

Proposed resolution (10/00):

Change the grammar specified in 13.5  over.oper paragraph 1 from

to




246. Jumps in function-try-block handlers

Section: 14.3  temp.arg     Status: DR     Submitter: Mike Miller     Date: 15 Sep 2000     Priority: 0     Drafting: Miller

From messages 8899-8902.

Is it permitted to jump from a handler of a function-try-block into the body of the function?

15  except paragraph 2 would appear to disallow such a jump:

A goto, break, return, or continue statement can be used to transfer control out of a try block or handler, but not into one.

However, 15.3  except.handle paragraph 14 mentions only constructors and destructors for the prohibition:

If the handlers of a function-try-block contain a jump into the body of a constructor or destructor, the program is ill-formed.

Is this paragraph simply reemphasizing the more general restriction, or does it assume that such a jump would be permitted for functions other than constructors or destructors? If the former interpretation is correct, it is confusing and should be either eliminated or turned into a note. If the latter interpretation is accurate, 15  except paragraph 2 must be revised.

(See also issue 98.)

Proposed resolution (04/01):

Delete 15.3  except.handle paragraph 14.




62. Unnamed members of classes used as type parameters

Section: 14.3.1  temp.arg.type     Status: DR     Submitter: Steve Adamczyk     Date: 13 Oct 1998     Drafting: Miller

Section 14.3.1  temp.arg.type paragraph 2 says

A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter.

It probably wasn't intended that classes with unnamed members should be included in this list, but they are arguably compounded from unnamed types.

Proposed resolution (04/01):

In 14.3.1  temp.arg.type paragraph 2, change

A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template-argument for a template type-parameter.

to

The following types shall not be used as a template-argument for a template type-parameter:



100. Clarify why string literals are not allowed as template arguments

Section: 14.3.2  temp.arg.nontype     Status: DR     Submitter: Mike Miller     Date: 9 Mar 1999

The explanation in 14.3.2  temp.arg.nontype paragraph 2 of why a string literal cannot be used as a template argument leaves something to be desired:

...because a string literal is an object with internal linkage.
I can't find anything that says that a string literal has internal linkage. In fact, I'd be pretty surprised if I did, since linkage is defined (in 3.5  basic.link ) strictly in terms of names, and a string literal doesn't have a name. Actually, I think that it's the namelessness of a string literal that prevents it from being a template argument; only the third and fourth bullets of 14.3.2  temp.arg.nontype paragraph 1 could conceivably apply, and both of those require that the entity have a name (i.e., that they be given as an id-expression).

Proposed Resolution (10/99): In 14.3.2  temp.arg.nontype paragraph 2, change

[Note: a string literal (2.13.4  lex.string ) is not an acceptable template-argument because a string literal is an object with internal linkage.
to
[Note: a string literal (2.13.4  lex.string ) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.



249. What is a member function template?

Section: 14.5.1.1  temp.mem.func     Status: DR     Submitter: David Thornley     Date: 11 Oct 2000

The phrase "member function template" is used in 3.2  basic.def.odr paragraph 5 in the list of entities whose definitions can appear more than once in a program, with a cross-reference to 14.5.1.1  temp.mem.func. The title of that section is "Member functions of class templates," and paragraph 1 of that section says,

A member function template may be defined outside of the class template in which it is declared.

The example in that paragraph shows a non-template member function of a class template being defined. This gives the impression that the phrase "member function template" is intended to refer to a member function of a class template.

If this usage were intended, much of the rest of the Standard would be unintelligible: objects of class template specializations could not be copied (12.8  class.copy paragraph 3), member functions of class templates could not be declared virtual (14.5.2  temp.mem paragraph 3), etc.

Suggested resolution:

Change "member function template" to "member function of a class template" in both 3.2  basic.def.odr paragraph 5 and 14.5.1.1  temp.mem.func paragraph 1.

(See also issue 205.)

Proposed resolution (10/00): As suggested.




116. Equivalent and functionally-equivalent function templates

Section: 14.5.5.1  temp.over.link     Status: DR     Submitter: Mike Miller     Date: 11 May 1999

From reflector message 8060.

14.5.5.1  temp.over.link , paragraphs 5 and 6, describes equivalence and functional equivalence for expressions involving template parameters. As a note in paragraph 5 points out, such expressions may involve type parameters as well as non-type parameters.

Paragraph 7, however, describes the equivalence of function templates only with respect to non-type template parameters. It appears to be unspecified how to determine the equivalence of template functions whose types involve expressions that use template type parameters.

    template <int I> struct S { };

    // The following two declarations are equivalent:
    template <int I> void f(S<I>);
    template <int J> void f(S<J>);

    // The IS doesn't say whether these are equivalent:
    template <class T> void f(S<sizeof(T)>);
    template <class T> void f(S<sizeof(T)>);

Proposed resolution (10/99): Remove the three uses of the words "non-type" in 14.5.5.1  temp.over.link paragraph 7.




120. Nonexistent non-terminal qualified-name

Section: 14.6  temp.res     Status: DR     Submitter: Bill Gibbons     Date: 28 May 1999

In 14.6  temp.res , references to the nonexistent syntactic non-terminal qualified-name occur twice in paragraph 3, twice in paragraph 4, and once in paragraph 5. There is also a reference in 14.1  temp.param paragraph 2.

Proposed resolution (10/99): Change the reference in all these cases to qualified-id.




121. Dependent type names with non-dependent nested-name-specifiers

Section: 14.6  temp.res     Status: DR     Submitter: Bill Gibbons     Date: 28 May 1999     Drafting: Miller

The wording in 14.6  temp.res paragraph 3:

A qualified-name that refers to a type and that depends on a template-parameter (14.6.2  temp.dep ) shall be prefixed by the keyword typename to indicate that the qualified-name denotes a type, forming an elaborated-type-specifier (7.1.5.3  dcl.type.elab ).
was intended to say:
A qualified-id that refers to a type and in which the nested-name-specifier depends on a template-parameter (14.6.2  temp.dep ) shall ...
in much the same vein as 14.6.2.1  temp.dep.type, second bullet, first half.

Proposed resolution (10/00): As suggested.




183. typename in explicit specializations

Section: 14.6  temp.res     Status: DR     Submitter: John Spicer     Date: 9 Nov 1999     Drafting: Spicer

From reflector messages 8353-5.

John Spicer: In 14.6  temp.res paragraph 5, the standard says

The keyword typename shall only be used in template declarations and definitions...
My understanding of the intent of this restriction is to say that typename is only allowed in contexts in which template dependent names can be found, but the wording leaves open to interpretation whether typename is allowed in an explicit specialization, such as:
    template <class T> struct A {};
    template <class T> struct B { typedef int X; };
    template <> struct A<int> {
        typename B<int>::X x;
    };
My understanding is that such usage is not permitted. This should be clarified one way or the other.

Mike Miller: I agree with your understanding that you are not allowed to use typename in an explicit specialization. However, I think the standard already says that — an explicit specialization is not a template declaration. According to the grammar in 14  temp paragraph 1, a template-declaration must have a non-empty template-parameter-list.

Nathan Myers: Is there any actual reason for this restriction? Its only apparent effect is to make it harder to specialize templates, with no corresponding benefit.

Proposed resolution (10/00):

In 14.6  temp.res paragraph 5, replace

The keyword typename shall only be applied to qualified names, but those names need not be dependent.

with

The keyword typename shall be applied only to qualified names, but those names need not be dependent. The keyword typename shall be used only in contexts in which dependent names can be used. This includes template declarations and definitions but excludes explicit specialization declarations and explicit instantiation declarations.



213. Lookup in dependent base classes

Section: 14.6.2  temp.dep     Status: DR     Submitter: John Spicer     Date: 10 Mar 2000     Drafting: Spicer

From reflector messages 8595-9.

Paragraphs 3-4 of 14.6.2  temp.dep say, in part,

if a base class of [a class] template depends on a template-parameter, the base class scope is not examined during name lookup until the class template is instantiated... If a base class is a dependent type, a member of that class cannot hide a name declared within a template, or a name from the template's enclosing scope.

John Spicer: The wording in paragraph 4 seems particularly odd to me. It essentially changes the order in which scopes are considered. If a scope outside of the template declares a given name, that declaration hides entities of the same name from template dependent base classes (but not from nondependent base classes).

In the following example, the calls of f and g are handled differently because B::f cannot hide ::f, but B::g doesn't try to hide anything, so it can be called.

    extern "C" int printf(char *, ...);
    template <class T> struct A : T {
        void h(T t) {
            f(t);  // calls ::f(B)
            g(t);  // calls B::g
        }
    };

    struct B {
        void f(B){printf("%s", "in B::f\n");}
        void g(B){printf("%s", "in B::g\n");}
    };

    void f(B){printf("%s", "in ::f\n");}

    int main()
    {
        A<B> ab;
        B b;
        ab.h(b);
    }

I don't think the current wording in the standard provides a useful facility. The author of class A can't be sure that a given call is going to call a base class function unless the base class is explicitly specified. Adding a new global function could cause the program to suddenly change meaning.

What I thought the rule was is, "If a base class is a dependent type a member of that class is not found by unqualified lookup".

Derek Inglis: My understanding is the same except that I'd remove the word "qualified" from your sentence.

Erwin Unruh: My interpretation is based on 14.6.4  temp.dep.res and especially 14.6.4.2  temp.dep.candidate (and largely on my memory of the discussions). For all unqualified names you do something like the following algorithm:

  1. check whether it is a dependent function call
  2. Do a lookup in the definition context and remember what you found there
  3. Do a Koenig-Lookup at instantiation time
  4. perform overloading if necessary

Regarding names from base classes you cannot find them in 2) because you don't know what base class you have. You cannot find them in 3) because members of classes are not found by Koenig lookup (only namespaces are considered). So you don't find them at all (for unqualified names).

For a qualified name, you start lookup for each 'part' of the qualification. Once you reach a dependent part, you stop and continue lookup at the instantiation point. For example:

    namespace A {
      namepace B {
	template <class T> class C {
	  template <class U> class D {
	    typedef int E;
	    // ...
	  };
	};
      };
    };

    template <class T> class F : public T {
      typename A::B::C<int>::D<T>::E var1;
      typename A::B::C<T>::D<int>::E var2;
      typename F::T::X var3;
    }

For var1 you do lookup for A::B::C<int>::D at definition time, for var2 you only do lookup for A::B::C. The rest of the lookup is done at instantiation time since specialisations could change part of the lookup. Similarly the lookup for var3 stops after F::T at definition time.

My impression was that an unqualified name never refers to a name in a dependent base class.

(See also issue 197.)

Proposed resolution (10/00):

  1. In 14.6.2  temp.dep paragraph 3, replace

    In the definition of a class template or in the definition of a member of such a template that appears outside of the template definition, if a base class of this template depends on a template-parameter, the base class scope is not examined during name lookup until the class template is instantiated.

    with

    In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.
  2. Remove from 14.6.2  temp.dep paragraph 4:

    If a base class is a dependent type, a member of that class cannot hide a name declared within a template, or a name from the template's enclosing scopes.



108. Are classes nested in templates dependent?

Section: 14.6.2.1  temp.dep.type     Status: DR     Submitter: Mark Mitchell     Date: 14 Apr 1999     Drafting: Spicer

From messages 8011 and 8016.

Mark Mitchell (via John Spicer): Given:

  template <class T> struct S {
     struct I1 {
       typedef int X;
     };
     struct I2 : public I1 {
        X x;
     };
  };

Is this legal? The question really boils down to asking whether or not I1 is a dependent type. On the one hand, it doesn't seem to fit any of the qualifications in 14.6.2.1  temp.dep.type . On the other, 14.7.3  temp.expl.spec allows explicit specialization of a member class of a class template, so something like:

  template <> 
  struct S<double>::I1 {
     int X;
  };

is apparently legal. But, then, `X' no longer refers to a type name. So, it seems like `I1' should be classified as dependent. What am I missing?

Erwin Unruh: I wrote that particular piece of text and I just missed the problem above. It is intended to be a dependent type. The reasoning is that I1 is just a shorthand for S<T>::I1 which clearly is dependent.

Suggested Resolution: (Erwin Unruh)

I think the list of what is a dependent type should be extended to cover "a type declared and used within the same template" modulo of phrasing.

(See also paper J16/00-0009 = WG21 N1231. This issue is also somewhat related to issue 205: classes nested inside template classes are, in some sense, "templates," just as non-template member functions of class templates and static data members of class templates are "templates.")

Proposed resolution (10/00):

Add after 14.6.1  temp.local paragraph 2:

Within the scope of a class template, when the unqualified name of a nested class of the class template is referred to, it is equivalent to the name of the nested class qualified by the name of the enclosing class template. [Example:
    template <class T> struct A {
	class B {};
	// B is equivalent to A::B, which is equivalent to A<T>::B,
	// which is dependent.
	class C : B { };
    };
end example]



206. Semantic constraints on non-dependent names

Section: 14.6.3  temp.nondep     Status: DR     Submitter: Mike Miller     Date: 23 Feb 2000     Drafting: Spicer

At what point are semantic constraints applied to uses of non-dependent names in template definitions? According to 14.6.3  temp.nondep , such names are looked up and bound at the point at which they are used, i.e., the point of definition and not the point of instantiation. However, the text does not mention the checking of semantic constraints.

Contrast this omission with the treatment of names in default argument expressions given in 8.3.6  dcl.fct.default paragraph 5, where the treatment of semantic constraints is explicit:

The names in the expression are bound, and the semantic constraints are checked, at the point where the default argument expression appears.
The following code is an example of where this distinction matters:
    struct S;

    template <class T> struct Q {
        S s;    // incomplete type if semantic constraints
                // are applied in the definition context
    };

    struct S { };

    // Point of instantiation of Q<int>; S is complete here

    Q<int> si;        
There is real-world code that depends on late checking of semantic constraints. The Standard should be explicit about whether this code is broken or not.

Proposed resolution (10/00):

In 14.6  temp.res paragraph 7, add the following immediately preceding the note:

If a type used in a non-dependent name is incomplete at the point at which a template is defined but is complete at the point at which an instantiation is done, and if the completeness of that type affects whether or not the program is well-formed or affects the semantics of the program, the program is ill-formed; no diagnostic is required.



22. Template parameter with a default argument that refers to itself

Section: 14.6.4  temp.dep.res     Status: DR     Submitter: unknown     Date: unknown

(Previously numbered 933.)

14.1  temp.param paragraph 13 says:

The scope of a template-parameter extends from its point of declaration until the end of its template. In particular, a template-parameter can be used in the declaration of subsequent template-parameters and their default arguments.
Is the following well-formed?
    template<class U = U> class X { ... };

[Note: this issue is resolved by the resolution of issue 187.]




24. Errors in examples in 14.7.3

Section: 14.7.3  temp.expl.spec     Status: DR     Submitter: unknown     Date: unknown

(Previously numbered 935; revised by Robert Klarer.)

Problem Description: At least four of the examples in 14.7.3  temp.expl.spec have errors.

Proposed Resolution (10/99):

1. Change the example in paragraph 8 from:

[Example:
    // file #1 
    #include <vector> 
    // Primary class template vector
    export template<class T> void f(t) { 
        vector<T> vec;         // should match the specialization
        /* ... */ 
    } 

    // file #2 
    #include <vector> 
    class B { }; 
    // Explicit specialization of vector for vector<B>
    template<class T> class vector<B> { /* ... */ } 
    template<class T> void f(T); 
    void g(B b) { 
        f(b);                   // ill formed: 
                                // f<B> should refer to vector<B>, but the 
                                // specialization was not declared with the 
                                // definition of f in file #1 
    } 
—end example]
to:
[Example:
    // file #1 
    #include <vector> 
    // Primary class template vector
    export template<class T> void f(T) { 
        std::vector<T> vec;     // should match the specialization 
        /* ... */ 
    }; 

    // file #2 
    #include <vector> 
    class B { }; 
    // Explicit specialization of vector for vector<B>
    namespace std { 
        template<> class vector<B> { /* ... */ }; 
    } 
    template<class T> void f(T); 
    void g(B b) { 
        f(b);                   // ill formed: 
                                // f<B> should refer to vector<B>, but the 
                                // specialization was not declared with the 
                                // definition of f in file #1 
    } 
—end example]

2. The example in paragraph 16 as it appears in the IS:

[Example:
    template<class T> struct A { 
        void f(T); 
        template<class X> void g(T, X); 
        void h(T) { } 
    }; 

    // specialization 
    template<> void A<int>::f(int); 

    // out of class member template definition 
    template<class T> template<class X> void A<T>::g(T,X) { } 

    // member template partial specialization 
    template<> template<class X> void A<int>::g(int, X); 

    // member template specialization 
    template<> template<> 
        void A<int>::g(int, char);        // X deduced as char
    template<> template<> 
        void A<int>::g<char>(int, char);  // X specified as char

    // member specialization even if defined in class definition 
    template<> void A<int>::h(int) { } 
end example]
The word 'partial' in the third comment in the example should be removed because this example does not illustrate partial specialization. Also, the two specializations of template<> template<> void A<int>::g(int, char); violate 14.7  temp.spec , paragraph 5, which reads:
No program shall explicitly instantiate any template more than once, both explicitly instantiate and explicitly specialize a template, or specialize a template more than once for a given set of template-arguments. An implementation is not required to diagnose a violation of this rule.
Proposed resolution (10/99):
[Example:
    template<class T> struct A { 
        void f(T); 
        template<class X1> void g1(T, X1); 
        template<class X2> void g2(T, X2); 
        void h(T) { } 
    }; 

    // specialization 
    template<> void A<int>::f(int); 

    // out of class member template definition 
    template<class T> template<class X1> void A<T>::g1(T,X1) { } 

    // member template specialization 
    template<> template<class X1> void A<int>::g1(int, X1); 

    // member template specialization 
    template<> template<> 
        void A<int>::g1(int, char);        // X1 deduced as char 
    template<> template<> 
        void A<int>::g2<char>(int, char);  // X2 specified as char 

    // member specialization even if defined in class definition 
    template<> void A<int>::h(int) { } 
end example]

3. Remove the spurious semicolon (or the curly brackets) from the end of the last line in the example in paragraph 17. This is the example as it appears in the IS:

[Example:
    template<class T1> class A { 
        template<class T2> class B { 
            void mf(); 
        };
    };
    template<> template<> A<int>::B<double> { };
    template<> template<> void A<char>::B<char>::mf() {};
end example]
Proposed resolution (10/99):
[Example:
    template<class T1> class A { 
        template<class T2> class B { 
            void mf(); 
        };
    };
    template<> template<> A<int>::B<double>;
    template<> template<> void A<char>::B<char>::mf();
end example]

4. Remove spurious semicolons (or curly brackets) from the specializations of mf1 and mf2 in the example in paragraph 18. This is the text of the example as it appears in the IS:

[Example:
    template<class T1> class A { 
        template<class T2> class B { 
            template<class T3> void mf1(T3); 
            void mf2(); 
        }; 
    }; 
    template<> template<class X> 
        class A<int>::B { }; 
    template<> template<> template<class T> 
        void A<int>::B<double>::mf1(T t) { }; 
    template<class Y> template<> 
        void A<Y>::B<double>::mf2() { }; // ill-formed; B<double> is specialized but 
                                         // its enclosing class template A is not 
end example]
Proposed resolution (10/99):
[Example:
    template<class T1> class A { 
        template<class T2> class B { 
            template<class T3> void mf1(T3); 
            void mf2(); 
        }; 
    }; 
    template<> template<class X> 
        class A<int>::B { }; 
    template<> template<> template<class T> 
        void A<int>::B<double>::mf1(T t) { }
    template<class Y> template<> 
        void A<Y>::B<double>::mf2() { } // ill-formed; B<double> is specialized but 
                                         // its enclosing class template A is not 
end example]



44. Member specializations

Section: 14.7.3  temp.expl.spec     Status: DR     Submitter: Nathan Myers     Date: 19 Sep 1998     Drafting: Spicer

From reflector message core-7851.

Some compilers reject the following:

    struct A {
        template <int I> void f();
        template <> void f<0>();
    };
on the basis of 14.7.3  temp.expl.spec paragraph 2:
An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member. An explicit specialization of a member function, member class or static data member of a class template shall be declared in the namespace of which the class template is a member. ...
claiming that the specialization above is not "in the namespace of which the enclosing class ... is a member". Elsewhere, declarations are sometimes required to be "at" or "in" "namespace scope", which is not what it says here. Paragraph 17 says:
A member or a member template may be nested within many enclosing class templates. If the declaration of an explicit specialization for such a member appears in namespace scope, the member declaration shall be preceded by a template<> for each enclosing class template that is explicitly specialized.
The qualification "if the declaration ... appears in namespace scope", implies that it might appear elsewhere. The only other place I can think of for a member specialization is in class scope.

Was it the intent of the committee to forbid the construction above? (Note that A itself is not a template.) If so, why?

Proposed resolution (04/01): In-class specializations of member templates are not allowed. In 14.7.3  temp.expl.spec paragraph 17, replace

If the declaration of an explicit specialization for such a member appears in namespace scope...
with
In an explicit specialization for such a member...

Notes from 04/00 meeting:

This issue was kept in "review" status for two major reasons:

  1. It's not clear that a change is actually needed. All uses of the phrase "in the namespace" in the IS mean "directly in the namespace," not in a scope nested within the namespace.
  2. There was substantial sentiment for actually adding support for in-class specializations at a future time, and it might be perceived as a reversal of direction to pass a change aimed at reinforcing the absence of the feature, only to turn around afterward and add it.

Notes from 10/00 meeting:

The core working group felt that the value of additional clarity here outweighs the potential disadvantages that were noted at the preceding meeting.




64. Partial ordering to disambiguate explicit specialization

Section: 14.7.3  temp.expl.spec     Status: DR     Submitter: Steve Adamczyk     Date: 13 Oct 1998     Drafting: Spicer

Paragraph 12 should address partial ordering. It wasn't updated when that change was made and conflicts with 14.5.5.2  temp.func.order paragraph 1.

Proposed resolution (10/00):

Remove 14.7.3  temp.expl.spec paragraph 12 and the example that follows.




241. Error in example in 14.8.1

Section: 14.8.1  temp.arg.explicit     Status: DR     Submitter: Mike Miller     Date: 9 Aug 2000

14.8.1  temp.arg.explicit paragraph 6 contains the following example:

    namespace A {
        struct B { };
        template<int X> void f();
    }
    namespace C {
        template<class T> void f(T t);
    }
    void g(A::B b) {
        f<3>(b);    // ill-formed: not a function call
        A::f<3>(b); // well-formed
        C::f<3>(b); // ill-formed; argument dependent lookup
                    // only applies to unqualified names
        using C::f;
        f<3>(b);    // well-formed because C::f is visible; then
                    // A::f is found by argument dependent lookup
    }

A::f() should have a parameter of type A::B.

Proposed resolution (10/00):

In the example in 14.8.1  temp.arg.explicit paragraph 6, change the third line from

        template <int X> void f();

to

        template <int X> void f(B);



70. Is an array bound a nondeduced context?

Section: 14.8.2.4  temp.deduct.type     Status: DR     Submitter: Jack Rouse     Date: 29 Sep 1998     Drafting: Spicer

Paragraph 4 lists contexts in which template formals are not deduced. Were template formals in an expression in the array bound of an array type specification intentionally left out of this list? Or was the intent that such formals always be explicitly specified? Otherwise I believe the following should be valid:

    template <int I> class IntArr {};

    template <int I, int J>
    void concat( int (&d)[I+J], const IntArr<I>& a, const IntArr<J>& b ) {}

    int testing()
    {
        IntArr<2> a;
        IntArr<3> b;
        int d[5];

        concat( d, a, b );
    }
Can anybody shed some light on this?

From John Spicer:

Expressions involving nontype template parameters are nondeduced contexts, even though they are omitted from the list in 14.8.2.4  temp.deduct.type paragraph 4. See 14.8.2.4  temp.deduct.type paragraphs 12-14:

  1. A template type argument cannot be deduced from the type of a non-type template-argument.

     ...

  1. If, in the declaration of a function template with a non-type template-parameter, the non-type template-parameter is used in an expression in the function parameter-list, the corresponding template-argument must always be explicitly specified or deduced elsewhere because type deduction would otherwise always fail for such a template-argument.

Proposed resolution (04/01): In 14.8.2.4  temp.deduct.type paragraph 4, add a third bullet:




181. Errors in template template-parameter example

Section: 14.8.2.4  temp.deduct.type     Status: DR     Submitter: John Spicer     Date: 4 Nov 1999

From reflector message 8346.

14.8.2.4  temp.deduct.type paragraph 18 uses incorrect syntax. Instead of

    template <template X<class T> > struct A { };
    template <template X<class T> > void f(A<X>) { }
it should be
    template <template <class T> class X> struct A { };
    template <template <class T> class X> void f(A<X>) { }

Proposed resolution (10/00): As suggested.




98. Branching into try block

Section: 15  except     Status: DR     Submitter: Jack Rouse     Date: 23 Feb 1999     Drafting: Vandevoorde

At the top of clause 15, in paragraph 2, it says:

A goto, break, return, or continue statement can be used to transfer control out of a try block or handler, but not into one.
What about switch statements?
    switch ( f() )
    {
    case 1:
         try {
             g();
    case 2:
             h();
         }
         catch (...)
         {
             // handler
         }
    break;
    }
Daveed Vandevoorde: (core-8045)

Consider:

    void f() {
        try {
        label:
            ;
        } catch(...) {
            goto label;
        }
    }
Now the phrase "try block" (without a hyphen) is used in paragraph 1 in a way that causes me to think that it is not intended to include the corresponding handlers. On the other hand, the grammar entity "try-block" (with hyphen) does include the handlers. So is the intent to prohibit the above or not?

Proposed resolution (10/00:

Change text in 15  except paragraph 2 from:

A goto, break, return, or continue statement can be used to transfer control out of a try block or handler, but not into one.
to:
A goto or switch statement shall not be used to transfer control into a try block or into a handler.
[ Example:
void f() {
  goto l1;  // Ill-formed
  goto l2;  // Ill-formed
  try {
    goto l1;  // OK
    goto l2;  // Ill-formed
    l1: ;
  } catch (...) {
    l2: ;
    goto l1;  // Ill-formed
    goto l2;  // OK
  }
}
end example ]
A goto, break, return, or continue statement can be used to transfer control out of a try block or handler.

(See also issue 246.)




208. Rethrowing exceptions in nested handlers

Section: 15.1  except.throw     Status: DR     Submitter: Bill Wade     Date: 28 Feb 2000     Drafting: Merrill

Paragraph 7 of 15.1  except.throw discusses which exception is thrown by a throw-expression with no operand.

May an expression which has been "finished" (paragraph 7) by an inner catch block be rethrown by an outer catch block?

    catch(...)    // Catch the original exception
    {
      try{ throw; }    // rethrow it at an inner level
                       // (in reality this is probably
                       // inside a function)
      catch (...)
      {
      }   // Here, an exception (the original object)
          // is "finished" according to 15.1p7 wording

      // 15.1p7 says that only an unfinished exception
      // may be rethrown.
      throw;    // Can we throw it again anyway?  It is
                // certainly still alive (15.1p4).
    }

I believe this is ok, since the paragraph says that the exception is finished when the "corresponding" catch clause exits. However since we have two clauses, and only one exception, it would seem that the one exception gets "finished" twice.

Proposed resolution (04/01):

  1. In 15.1  except.throw paragraph 4, change

    When the last handler being executed for the exception exits by any means other than throw; ...
    to
    When the last remaining active handler for the exception exits by any means other than throw; ...

  2. In 15.1  except.throw paragraph 6, change

    A throw-expression with no operand rethrows the exception being handled.
    to
    A throw-expression with no operand rethrows the currently handled exception (15.3  except.handle).

  3. Delete 15.1  except.throw paragraph 7.

  4. Add the following before 15.1  except.throw paragraph 6:

    An exception is considered caught when a handler for that exception becomes active (15.3  except.handle). [Note: an exception can have active handlers and still be considered uncaught if it is rethrown.]

  5. Change 15.3  except.handle paragraph 8 from

    An exception is considered handled upon entry to a handler. [Note: the stack will have been unwound at that point.]
    to

    A handler is considered active when initialization is complete for the formal parameter (if any) of the catch clause. [Note: the stack will have been unwound at that point.] Also, an implicit handler is considered active when std::terminate() or std::unexpected() is entered due to a throw. A handler is no longer considered active when the catch clause exits or when std::unexpected() exits after being entered due to a throw.

    The exception with the most recently activated handler that is still active is called the currently handled exception.

  6. In 15.3  except.handle paragraph 16, change "exception being handled" to "currently handled exception."




210. What is the type matched by an exception handler?

Section: 15.3  except.handle     Status: DR     Submitter: Scott Douglass     Date: 6 Mar 2000     Drafting: Merrill

15.3  except.handle paragraph 3 says,

A handler is a match for a throw-expression with an object of type E...

This wording leaves it unclear whether it is the dynamic type of the object being thrown or the static type of the expression that determines whether a handler is a match for a given exception. For instance,

    struct B { B(); virtual ~B(); };
    struct D : B { D(); };
    void toss(const B* b) { throw *b; }
    void f() { const D d; toss(&d); }

In this code, presumably the type to be matched is B and not const D (15.1  except.throw).

Suggested resolution: Replace the cited wording as follows:

A handler is a match for a throw-expression which initialized a temporary (15.1  except.throw) of type E...

Proposed resolution (10/00):

  1. Change 15.1  except.throw paragraph 3 from

    A throw-expression initializes a temporary object, the type of which is determined...

    to

    A throw-expression initializes a temporary object, called the exception object, the type of which is determined...
  2. Change 15.3  except.handle paragraph 3 from

    A handler is a match for a throw-expression with an object of type E if...

    to

    A handler is a match for an exception object of type E if...



25. Exception specifications and pointers to members

Section: 15.4  except.spec     Status: DR     Submitter: unknown     Date: unknown

(Previously numbered 936.)

15.4  except.spec paragraph 3 should say what happens when two pointers to members with different exception specifications are assigned to each other, initialized with one another, etc.

Proposed Resolution (04/99): Change the text in 15.4  except.spec paragraph 3 from:

Similarly, any function or pointer to function assigned to, or initializing, a pointer to function shall only allow exceptions that are allowed by the pointer or function being assigned to or initialized.
to:
A similar restriction applies to assignment to and initialization of pointers to functions, pointers to member functions, and references to functions: the target entity shall allow at least the exceptions allowed by the source value in the assignment or initialization.



87. Exception specifications on function parameters

Section: 15.4  except.spec     Status: DR     Submitter: Steve Adamczyk     Date: 25 Jan 1999     Drafting: Vandevoorde

In 15.4  except.spec paragraph 2:

An exception-specification shall appear only on a function declarator in a function, pointer, reference or pointer to member declaration or definition.
Does that mean in the top-level function declarator, or one at any level? Can one, for example, specify an exception specification on a pointer-to-function parameter of a function?
    void f(int (*pf)(float) throw(A))
Suggested answer: no. The exception specifications are valid only on the top-level function declarators.

However, if exception specifications are made part of a function's type as has been tentatively agreed, they would have to be allowed on any function declaration.

There is already an example of an exception specification for a parameter in the example in 15.4  except.spec paragraph 1.

Proposed resolution (04/01): Change text in 15.4  except.spec paragraph 1 from:

An exception-specification shall appear only on a function declarator in a function, pointer, reference or pointer to member declaration or definition.
to:
An exception-specification shall appear only on a function declarator for a function type, pointer to function type, reference to function type, or pointer to member function type that is the top-level type of a declaration or definition, or on such a type appearing as a parameter or return type in a function declarator.

(See also issues 25, 92, and 133.)




126. Exception specifications and const

Section: 15.4  except.spec     Status: DR     Submitter: Martin von Loewis     Date: 8 June 1999     Drafting: Miller

The standard is inconsistent about constness inside exception specifications.

    struct X {};
    struct Y:X {};

    const Y bar() {return Y();}

    void foo()throw(const X)
    {
      throw bar();
    }
It is unclear whether calling foo will result in a call to std::unexpected. According to 15.4  except.spec paragraph 7, only two cases are treated specially with regard to inheritance: If "class X" appears in the type-id-list, or if "class X*" appears in the type-id-list. Neither is the case here, so foo only allows exceptions of the same type (const X). As a result, std::unexpected should be called.

On the other hand, the intent of exception specification appears to allow an implementation of this example as

    void foo()
    try{
      throw bar();
    }catch(const X){
      throw;
    }catch(...){
      std::unexpected();
    }
According to 15.3  except.handle , this replacement code would catch the exception, so std::unexpected would not be called.

Suggested resolution: Change 15.4  except.spec paragraph 7 to read

A function is said to allow all exception objects of all types E for which one of the types T in the type-id-list would be a handler, according to 15.3  except.handle .

Proposed resolution (10/00):

Replace 15.4  except.spec paragraph 7 with the following:

A function is said to allow an exception of type E if its exception-specification contains a type T for which a handler of type T would be a match (15.3  except.handle) for an exception of type E.



145. Deprecation of prefix ++

Section: D.1  depr.post.incr     Status: DR     Submitter: Mike Miller     Date: 23 Jul 1999     Drafting: Miller

D.1  depr.post.incr indicates that use of the postfix ++ with a bool operand is deprecated. Annex D  depr says nothing about prefix ++. However, this use of prefix ++ is also deprecated, according to 5.3.2  expr.pre.incr paragraph 1. Presumably D.1  depr.post.incr should be expanded to cover prefix ++, or another section should be added to Annex D  depr.

Proposed resolution (10/00):

Change the entire section D.1  depr.post.incr, including its heading, to read as follows:

D.1 Increment operator with bool operand [depr.incr.bool]

The use of an operand of type bool with the ++ operator is deprecated (see 5.3.2  expr.pre.incr and 5.2.6  expr.post.incr).




131. Typo in Lao characters

Section: extendid     Status: DR     Submitter: John Spicer     Date: 23 June 1999

From reflector messages 8147 and 8153.

The Lao character 0e0d should be 0e8d. 0e0d is both out of order and already used in the Thai characters.

Proposed resolution (10/99): As suggested.